I am trying to subclass a class (Y) from an external library that can not be changed. It has a method as such:
protected Object doSomething(Class<?> clazz)
Now in my Scala class (X) I am extending this class and trying to override the method.
override protected def doSomething(clazz: Class[_ <: Object]): AnyRef
However this results in 'Method doSomething overrides nothing'. Changing the code to:
override protected def doSomething(clazz: Class[_]): AnyRef
Stops this error but now when the class is compiled it results in the following error:
class X needs to be abstract, since method doSomething in class Y of type (x$1: Class[_ <: Object])Object is not defined
(Note that Class[_ <: T] does not match Class[_]: their type parameters differ)
Is there any way to achieve this? Scala Version: 2.12.1, Java Version 8.
In the question you did not mention that Object doSomething(Class<?> clazz) method is the implementation of abstract T doSomething(Class<? extends T> clazz) method of abstract generic class.
So actually you have following Java classes (I renamed doSomething to test for brevity):
public abstract class AbstractJava<T> {
protected abstract T test(Class<? extends T> c);
}
public class ConcreteJava extends AbstractJava<Object> {
#Override
protected Object test(Class<?> c) {
return null;
}
}
And you are trying to implement following Scala class:
class ConcreteScala extends ConcreteJava {
override protected def test(c: Class[_]) = super.test(c)
}
But compilation fails because when you try to override test() Scala treats ConcreteJava.test() and AbstractJava.test() methods as if they have different signature.
I found the following workaround.
Create additional Java class that by overriding test() "renames" it to renameTest() and also provides ability to call super ConcreteJava.test() through concreteTest() method.
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);
}
}
Now in ConcreteScala class you can override renameTest() and you're still able to call super ConcreteJava.test() method using concreteTest() method.
class ConcreteScala extends RenameJava {
override protected def renameTest(c: Class[_]) = {
// custom logic
concreteTest(c)
}
}
In your specific "Spring" case it's implemented in the following way.
RenameGsonHttpMessageConverter.java
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import java.io.IOException;
public abstract class RenameGsonHttpMessageConverter extends GsonHttpMessageConverter {
protected Object superReadInternal(Class<?> clazz, HttpInputMessage inputMessage) throws
IOException, HttpMessageNotReadableException {
return super.readInternal(clazz, inputMessage);
}
abstract protected Object renameReadInternal(Class<?> clazz, HttpInputMessage inputMessage) throws
IOException, HttpMessageNotReadableException;
#Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return renameReadInternal(clazz, inputMessage);
}
}
CustomGsonHttpMessageConverter.scala
import org.springframework.http.HttpInputMessage
class CustomGsonHttpMessageConverter extends RenameGsonHttpMessageConverter {
override protected def renameReadInternal(clazz: Class[_], inputMessage: HttpInputMessage) = {
// custom logic
// or you may want to call
superReadInternal(clazz, inputMessage)
}
}
Also I made a bug report SI-10155.
Related
I thought putting final in Function would stop this issue from happening? I'm not too familiar with Scala. Can someone explain?
Class 'Country must either be declared abstract or implement abstract member 'execute():Object' in path.to.Invokable'
class Country extends MarketFunction("america") {}
abstract class MarketFunction(function: String) extends Function {
...
}
trait Function extends Invokable {
final def execute(): AnyRef = {
// not important
}
}
public interface Invokable {
Object execute();
}
Is it because AnyRef is not a direct comparison to java Object?
This worked for me
class Country extends MarketFunction("america") {}
abstract class MarketFunction(function: String) extends Function[AnyRef] {
...
}
trait Function[A <: AnyRef] extends Invokable {
override final def execute(): A = {
// not important
}
}
public interface Invokable {
Object execute();
}
How can I make arbitrary type T to conform to trait Run without having inherit the trait? Type class works at method level but class is expecting type Run and I cannot change method signature of run() as it is overriding an interface.
trait Run[T] {
def run(): Unit
}
class SomeClass(runner: Run[_]) extends Runnable {
#override def run() {
runner.run()
}
}
Current solution:
class SomeRunnable(someRunnable: SomeType) {
#override def run(){
someRunabble.someMethod()
}
}
So I have a Java method:
public abstract List<Class<? extends MyClass>> getListOfClasses();
and I need to override it in Scala. Here is how I am currently doing it:
override def getListOfClasses: java.util.List[Class[_ <: MyClass[_]]] = { null }
However, this does not compile, and I get the error: method getListOfClasses has incompatible type
What am I doing wrong?
EDIT:
MyClass is defined in Java like this:
abstract class MyClass[T] extends MyOtherClass[T] {...}
FINAL EDIT:
As suggested by #AlexeyRomanov below, changing my Java method return type fixed the problem:
public abstract List<Class<? extends MyClass<?>>> getListOfClasses();
It's ugly but asInstanceOf works.
This ugliness comes from Java's inability to express covariance and contra-variance.
Interface:
import java.util.List;
public abstract class Game2J {
public interface MyClass {}
public abstract List<Class<? extends MyClass>> getListOfClasses();
}
Scala code:
import java.util
import Game2J.MyClass
import scala.collection.JavaConverters._
class Game2S extends Game2J {
class MyClass1 extends MyClass
class MyClass2 extends MyClass
override def getListOfClasses: util.List[Class[_ <: MyClass]] =
List(classOf[MyClass1], classOf[MyClass2]).asJava.asInstanceOf[util.List[Class[_ <: MyClass]]]
}
Working example:
public class Game2 {
public static void main(String[] args) {
new Game2S().getListOfClasses().stream().forEach((cl) -> System.out.println(cl.getSimpleName()));
}
}
In Scala, I need to override the following, given, Java classes and methods:
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)
}
I'm getting the compilation error:
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)
I wouldn't expect the Scala compiler to refer to an abstract method on a super call. Also, I'd expect it to refer to the direct super class first.
How can I make the Scala class compile?
Thanks!
Edit:
When leaving off the super.test(clazz) call, there'll be the error message:
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
Well, of course these are the same types (or variants) ...! - So there's something wrong with Scala/Java inheritance ...
Thanks to michid, there's a preliminary solution:
class ConcreteScala3 {
this: ConcreteJava =>
protected override def test(clazz: Class[_ <: AnyRef]): AnyRef = {
this.foo() // method of ConcreteJava
null
}
}
although we can't make super calls from here.
Responses are still most welcome.
There are some limitations when overriding Java methods with raw types. See the corresponding Scala ticket. Specifically Martin Odersky's comment: "[...] The only thing one can do in these situations is implement a subclass in Java that implements the method. [...]"
However, I pointed out in a blog post earlier that there seems to be a solution for certain cases. The trick is to explicitly declare the self type of the overriding Scala class using an existential type for the raw type on the Java side.
With this technique I got the following working:
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)
}
}
The question about the same issue was raised again in 2017.
I think that this is certainly a bug and I created an issue SI-10155.
You can apply the following workaround.
Create additional Java class that by overriding test() "renames" it to renameTest() and also provides ability to call super ConcreteJava.test() through concreteTest() method.
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);
}
}
Now in ConcreteScala class you can override renameTest() and you're still able to call super ConcreteJava.test() method using concreteTest() method.
class ConcreteScala extends RenameJava {
override protected def renameTest(c: Class[_]) = {
// custom logic
concreteTest(c)
}
}
I have some classes with a protected constructor and the factory method is inside the companion object of an abstract super class. As of Scala 2.9.0.RC4 this doesn't compile anymore. I have "fixed" the issue by making the constructors package protected. But I don't want other classes even inside the same package to be able to call the constructors.
So what should I?
sealed abstract class A
object A {
//the factory method, returning either a B or C
def apply(): A
}
class B protected (...) extends A
class C protected (...) extends A
You could make them private inner classes of the object.
object A {
private class B extends A
private class C extends A
}
Since you need the classes accessible for pattern matching, I would suggest creating a new subpackage for them and making the constructor private to that package. Now only the import statements in your client code need to be changed.
sealed abstract class A {
}
package myPackage.subPackage {
object A {
def apply(): A = new B
}
class B private[subPackage] () extends A {
}
}
package other {
object Foo {
def foo {
myPackage.subPackage.A()
//does not compile: new myPackage.subPackage.B
}
}
}
Another option is to create companion objects for each implementation of A and delegate construction to a factory method in this object:
sealed abstract class A
object A {
//the factory method, returning either a B or C
def apply(): A = {
if (...) B()
else C()
}
}
object B {
def apply() : B = new B()
}
class B private (...) extends A
object C {
def apply() : C = new C()
}
class C private (...) extends A