2015-04-28 88 views
10

我有以下層次的問題階:斯卡拉泛型和繼承

class ScalaGenericTest { 
    def getValue[A, B <: Abstract[A]](clazz: B): A = clazz.a 

    def call: String = { 
    val sub: Subclass = new Subclass 
    getValue(sub) 
    } 
} 

class Subclass extends Abstract[String] { 
    def a: String = "STRING" 
} 

abstract class Abstract[A] { 
    def a: A 
} 

編譯器似乎並不能夠泛型參數綁定在調用getValue函數 - 我認爲它應該能夠從Subclass的定義中推斷出來。編譯錯誤如下:

推斷類型參數[沒什麼,子類]不符合方法GetValue的類型參數界限[A,B <:摘要[A]

它的工作原理,如果我明確地將泛型類型參數傳遞給該方法,即getValue[String,Subclass](sub),但編譯器肯定能夠推斷出這一點?

相同的層次結構在Java中正常工作:

public class JavaGenericTest { 

    public <T,U extends Abstract<T>> T getValue(U subclass) { 
     return subclass.getT(); 
    } 

    public String call(){ 
     Subclass sub = new Subclass(); 
     return getValue(sub); 
    } 

    private static class Subclass extends Abstract<String> { 
     String getT(){ 
      return "STRING"; 
     } 
    } 

    private static abstract class Abstract<T> { 
     abstract T getT(); 
    } 
} 

我是很新,斯卡拉所以可能有一些細微之處,我很想念。

在此先感謝您的幫助!

回答

9

這是Scala類型推斷的限制。該問題在SI-2272(該示例中使用implicits,但顯式使用時發生相同的錯誤)中進行了描述。它已關閉,因爲不會修復

在這個問題上,Adriaan Moors建議避免在雙方都有類型變量的約束。即。 B <: Abstract[A]。簡單的解決方法是完全避免第二個類型參數。

def getValue[A](clazz: Abstract[A]): A = clazz.a 

scala> val sub = new Subclass 
sub: Subclass = [email protected] 

scala> getValue(sub) 
res11: String = STRING 

此外,阿德里安還提供了使用隱式<:<作爲另一種變通的方式。把它放在你的例子的情況下,它看起來像:

def getValue[A, B](b: B)(implicit ev: B <:< Abstract[A]): B = b.a 

凡通過Predef隱含提供的<:<一個實例。

+0

也可以與'乙<%摘要[A]'通用參數使用隱式圖。我認爲這可能會被棄用,但它產生的暗含的論證是類型推斷的充分證據。 –

+0

很好的答案。我確實考慮了隱含的論證方法,但沒有看到原始原因不起作用的充分理由。使用<%查看邊界肯定已被棄用。 – paulyb

1

至於除了Justin的和M-Z的答案,另一種方法,使類似的聲明同時兼顧兩個類型參數:

def getValue[A, B](clazz: B)(implicit evidence: B <:< Abstract[A]): A = clazz.a 
3

我在同一時間有同樣的問題了。並創造了大量隱含的證據來克服。然後我不小心考慮階集合API文檔,發現瞭解決方案:http://www.scala-lang.org/api/2.11.4/index.html#scala.collection.generic.GenericTraversableTemplate

class ScalaGenericTest { 
    def getValue[A, B[X] <: Abstract[X]](clazz: B[A]): A = clazz.a 

    def call: String = { 
    val sub: Subclass = new Subclass 
    getValue(sub) 
    } 
} 

class Subclass extends Abstract[String] { 
    def a: String = "STRING" 
} 

abstract class Abstract[A] { 
    def a: A 
}