3

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.

11
  • 1
    Please tell us jdk and scala versions Commented Jan 14, 2017 at 11:25
  • 1
    override def doSomething(clazz: Class[_]): AnyRef = ??? compiles fine with Scala 2.11.8 and JDK 8u91 Commented Jan 14, 2017 at 11:59
  • 1
    @marstran that results in the "overrides nothing" error again. Commented Jan 14, 2017 at 18:53
  • 1
    Cannot reproduce the second error. I placed public class Base { protected Object doSomething(Class<?> clazz) { ... } } in a Java file, compiled it, then put it on the REPL classpath. Then I typed class Sub extends Base { override def doSomething(clazz: Class[_]): AnyRef = ??? }, and it compiled fine. Same Scala/Java versions. Commented Jan 14, 2017 at 19:57
  • 1
    @mixel The library is Spring 4.2, the class (Y) is the GsonHttpMessageConverter that in turn extends from AbstractGenericHttpMessageConverter<Object> which is where the original "doSomething" method is defined. The method that I am trying to overwrite is readInternal(Class<?> clazz, HttpInputMessage inputMessage). The way I have fixed this so far is to extend from AbstractGenericHttpMessageConverter instead of GsonHttpMessageConverter, but this is not ideal really. Commented Jan 18, 2017 at 14:07

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.