Scala дженерики и наследование
У меня проблема со следующей иерархией в 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
}
Компилятор, похоже, не может связывать общий параметр A в вызове функции getValue - я думаю, что он должен иметь возможность сделать это из определения подкласса. Ошибка компиляции выглядит следующим образом:
аргументы inferred type [Nothing, Subclass] не соответствуют методам метода getValue типа [A, B <: 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, поэтому, возможно, есть некоторые тонкости, которые мне не хватает.
Заранее благодарим за помощь!
Ответы
Ответ 1
Это ограничение в выражении типа 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
Кроме того, Adriaan также предоставил возможность использовать неявный <:<
в качестве другой работы. Чтобы выразить это в контексте вашего примера, это будет выглядеть так:
def getValue[A, B](b: B)(implicit ev: B <:< Abstract[A]): B = b.a
Если экземпляр <:<
предоставляется неявно через Predef.
Ответ 2
У меня такая же проблема и в одно время. И создал большие скрытые доказательства, чтобы преодолеть. Впоследствии я случайно заглядываю в scala коллекцию api docs и нашел решение: 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
}
Ответ 3
Как дополнение к ответам Justin и m-z, еще один способ сделать аналогичную декларацию, сохраняющую два типа параметров:
def getValue[A, B](clazz: B)(implicit evidence: B <:< Abstract[A]): A = clazz.a