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)
}
}
Related
I am implementing an extension of ml.Transformer in Spark; but this question is Scala specific. Here is an example object (part of a Class/Object pair):
abstract class UDFTransformer(func: UserDefinedFunction,
inputFieldNames: Seq[String],
outputFieldName: String) extends Transformer with MLWritable with Serializable {
... definitions here ...
}
object UDFTransformer extends MLReadable[UDFTransformer] {
// Since there are no parameters associted with the UDF, there is nothing to save!
class Writer(instance: UDFTransformer) extends MLWriter {
override protected def saveImpl(path: String): Unit = {}
}
abstract protected class Reader extends MLReader[UDFTransformer]
override def read: MLReader[UDFTransformer] = new Reader
override def load(path: String): UDFTransformer = super.load(path)
}
The new Reader does not compile because the class is abstract and cannot be instantiated. But; any child class will have to define it; along with its necessary members. I cannot just make read abstract as well, this gives me a warning Only classes can have declared but undefined methods.
The fundamental problem is that each child class of my UDFTransformer is going to wrap a specific UDF. Therefore, the reader needs to be able to generate a specific UDF object; this can't be declared in the superclass. But this 'factory' belongs in the companion object, not in the abstract class itself.
How can I go about building a companion object for an abstract class that can leave the definition of read undefined?
The normal way to do it is by creating an abstract class or trait for the companion objects. Something like
abstract class UDFTransformerCompanion[T <: UDFTransformer] extends MLReadable[T] {
abstract def read: MLReader[T]
override def load(path: String): T = super.load(path)
}
class SomeTransformer extends UDFTransformer { ... }
object SomeTransformer extends UDFTransformerCompanion[SomeTransformer] {
override def read: MLReader[SomeTransformer] = ...
}
Not sure why you have the load = super.load override, and it doesn't look like you can have a companion object for the UDFTransformer itself, at least not one extending this abstract class.
See GenericTraversableTemplate for a standard library example.
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.
I would like to implement a trait with a self-type like the following:
trait HasEquipment {
this: {def loadEquipment: List[Equipment]} =>
//more methods
}
Now i can mix this trait into classes and these classes have to define a "loadEquipment" Method:
case class Course(id: Long) extends HasEquipment {
def loadEquipment: List[Equipment] = {
//implementation
}
}
However I don't want to expose to loadEquipment Method to everyone but only to the trait. So what i would like to do is make loadEquipment private in the Course class so that only the trait has access to it. Making it private prevents the trait from accessing the method.
Is there another way to make loadEquipment only accessible from within the trait?
Not sure why you want a self-type here. An abstract, protected member works just as well:
trait HasEquipment {
protected def loadEquipment: List[Equipment]
}
case class Course(id: Long) extends HasEquipment {
override protected def loadEquipment: List[Equipment] = ???
}
As a side note, structural types in Scala make use of reflection, which is the reason why they're usually avoided.
I'm having a Scala trait A with an abstract method
trait A[T] {
def self: T
}
Now it happened me that I want to extends a Java class not under my control with a field with the same name:
public class B<T> {
protected T self;
}
I'd like to have a class C defined like
class C[T] extends B[T] with A[T] {
override def self: T = /* the field `self` of `B` ??? */
}
Is it possible to access the field B.self somehow so that it doesn't resolve to the method A.self instead? Is it even possible to have a scenario like that?
I'm trying to implement a generic java interface in scala. I have looked at:
How do I extend Java interface containing generic methods in Scala?
And Scala: Overriding Generic Java Methods II
But still I could not find an answer. Here's the method signature from Spring web:
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
I tried the following in scala:
#throws(classOf[IOException])
#throws(classOf[HttpMessageNotReadableException])
override def read[T](clazz : Class[_ <: T], inputMessage : HttpInputMessage) : T ={
}
But I get an error saying that this method overrides nothing. If I erase the type by doing:
override def read(clazz : Class[_], inputMessage : HttpInputMessage) : AnyRef ={
It marks the method as being overwritten. My question is how can I keep type safety here and force it to override the interface method?
Regards
EDIT
The spring interface:
public interface HttpMessageConverter<T> {
T read(Class<? extends T> clazz,
HttpInputMessage inputMessage)
throws IOException,
HttpMessageNotReadableException
}
I think the problem is likely to be that you have added a type parameter to the read method, rather than using the type parameter from your class declaration:
class MyImpl[T] extends JavaInterface[T] {
override def read(clazz: Class[_ <: T], ... )
}
If we rename your T to U it becomes clearer what you have done:
class MyImpl[T] extends JavaInterface[T] {
/** U is not T */
override def read[U](clazz: Class[_ <: U], ... )
}
You might also try and sing "U is not T" to the tune of REM's Losing My Religion to hammer the point home.
In java you have parameterilized interface, but in scala you try to parameterize method.