Scala: переопределение общих методов Java II

В 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. Кроме того, я ожидаю, что сначала он обратится к первому суперклассу.

Как я могу скомпилировать класс 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.

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

Ответы

Ответ 1

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

Тем не менее, я ранее указывал в сообщении , что, как представляется, решение для некоторых случаев. Хитрость заключается в том, чтобы явно объявить тип self переопределяющего класса 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)
    }
}

Ответ 2

Вопрос об этой же проблеме был вновь поднят в 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)
  }
}