2015-04-28 244 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 
}