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)
}
}