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