scala泛型和继承

时间:2015-04-28 15:19:13

标签: scala generics

我在scala中遇到以下层次结构的问题:

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函数的调用中绑定泛型参数A - 我认为它应该能够从Subclass的定义中推断出这一点。编译错误如下:

  

推断类型参数[Nothing,Subclass]不符合方法getValue的类型参数边界[A,B&lt ;: Abstract [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();
    }
}

我对Scala很陌生,所以我可能会遗漏一些微妙之处。

提前感谢您的帮助!

3 个答案:

答案 0 :(得分: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 = Subclass@585cbda6

scala> getValue(sub)
res11: String = STRING

此外,Adriaan还提供了一种使用隐式<:<作为另一种解决方法的方法。将它放在您的示例的上下文中,它看起来像:

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

通过Predef隐式提供<:<的实例。

答案 1 :(得分:3)

我也同时遇到同样的问题。并创造了大量的隐含证据来克服。之后我不小心查看了scala集合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
}

答案 2 :(得分:1)

作为贾斯汀和m-z答案的补充,另一种使类似声明保留两种类型参数的方法:

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