Scala: переопределение общих методов Java II - PullRequest
15 голосов
/ 22 июня 2011

В Scala мне нужно переопределить следующие данные классы и методы Java:

public abstract class AbstractJava<T> {
    protected abstract T test(Class<? extends T> clazz);
}

public class ConcreteJava extends AbstractJava<Object> {
    @Override
    protected Object test(Class<?> clazz) {
        return null;
    }
}

// Scala
class ConcreteScala extends ConcreteJava {
    protected override def test(clazz: Class[_ <: AnyRef]): AnyRef =
        super.test(clazz)
}

Я получаю ошибку компиляции:

error: ambiguous reference to overloaded definition,

both method test in class ConcreteJava of type 
(clazz: java.lang.Class[_])java.lang.Object

and method test in class AbstractJava of type 
(clazz: java.lang.Class[_ <: java.lang.Object])java.lang.Object

match argument types (Class[_$1]) and expected result type AnyRef

super.test(clazz)

Я бы не ожидал, что компилятор Scala будет ссылаться на метод abstract при вызове super. Кроме того, я ожидаю, что сначала он будет ссылаться на суперкласс direct .

Как я могу сделать компиляцию класса Scala?

Спасибо!

Edit:

При прекращении вызова super.test(clazz) появится сообщение об ошибке:

error: name clash between defined and inherited member:

method test:(clazz: Class[_ <: AnyRef])AnyRef and
method test:(clazz: java.lang.Class[_])java.lang.Object in class ConcreteJava

have same type after erasure: (clazz: java.lang.Class)java.lang.Object

protected override def test(clazz: Class[_ <: AnyRef]): AnyRef = null

Ну, конечно это те же типы (или варианты) ...! - Что-то не так с наследованием Scala / Java ...

Благодаря michid , есть предварительное решение:

class ConcreteScala3 {
  this: ConcreteJava =>
  protected override def test(clazz: Class[_ <: AnyRef]): AnyRef = {
    this.foo() // method of ConcreteJava
    null
  }
}

хотя мы не можем делать super звонки отсюда.

Ответы по-прежнему приветствуются.

Ответы [ 2 ]

5 голосов
/ 23 июня 2011

Существуют некоторые ограничения при переопределении методов Java необработанными типами. См. Соответствующий билет Scala . В частности, комментарий Мартина Одерского : "[...] Единственное, что можно сделать в этих ситуациях, это реализовать в Java подкласс, который реализует метод. [...]"

Однако в своем блоге ранее я указывал, что, по-видимому, для некоторых случаев существует решение. Хитрость заключается в том, чтобы явно объявить собственный тип переопределяющего класса Scala, используя экзистенциальный тип для необработанного типа на стороне Java.

С помощью этой техники я получил следующую работу:

public abstract class AbstractJava<T> {
    protected abstract T test(Class<T> clazz);
}

public class ConcreteJava extends AbstractJava<Object> {
    @Override
    protected Object test(Class<Object> clazz) {
        return null;
    }
}

class ConcreteScala extends ConcreteJava {
    this: AbstractJava[AnyRef] =>

    protected override def test(clazz: Class[AnyRef]): AnyRef = {
        super.test(clazz)
    }
}
0 голосов
/ 20 января 2017

Вопрос о той же проблеме был вновь поднят в 2017 году.

Я думаю, что это, безусловно, ошибка, и я создал проблему SI-10155 .

Вы можете применить следующий обходной путь.

Создайте дополнительный класс Java, который путем переопределения test() «переименовывает» его в renameTest(), а также предоставляет возможность вызова метода super ConcreteJava.test() через concreteTest().

public abstract class RenameJava extends ConcreteJava {
    public Object concreteTest(Class<?> c) {
        return super.test(c);
    }

    abstract protected Object renameTest(Class<?> c);

    @Override
    protected Object test(Class<?> c) {
        return renameTest(c);
    }
}

Теперь в классе ConcreteScala вы можете переопределить renameTest(), и вы все равно сможете вызывать метод super ConcreteJava.test(), используя метод concreteTest().

class ConcreteScala extends RenameJava {
  override protected def renameTest(c: Class[_]) = {
    // custom logic
    concreteTest(c)
  }
}
...