views:

882

answers:

3

Suppose we have the following Java interface:

// Java
public interface Foo {
    <T> T bar(Class<T> c);
}

How should I extend it in Scala? Writing

// Scala
class FooString extends Foo {
  override def bar(c: Class[String]): String = "hello, world";
}

will cause the compiler to throw "class FooString needs to be abstract, since method bar in trait Foo of type [T](Class[T])T is not defined."

Thanks in advance!

Update: The ugly truth is: I've misunderstood generics in Java.

In any case, the solutions to my woes are shown in both Nicolas' and Walter's answers, although I prefer Walter's answer better 'cos it's less verbose.

Thanks Nicolas and Walter! :)

+1  A: 

You may change like this:

public interface Foo<T> {
    T bar(Class<T> c);
}

class FooString extends Foo[String] {
    override def bar(c: Class[String]): String = "hello, world";
}
Eastsun
Actually, the Java interface is a third party API, so I have to use it as it is.
shaolang
As far as I know, there is no way to override a generic method with a special type method.
Eastsun
+1  A: 

This works:

class FooString extends Foo {
  def bar[String](c: Class[String]): String = "hello world".asInstanceOf[String]
}

val fs = new FooString
println(fs.bar(classOf[String]))

Edited:

The comments from @Eastsun is correct. Since bar is a generic method with type parameter T, the implementation of bar in Scala has to be a generic method as well. I think the right way to implement Foo in Scala is the following:

class FooString extends Foo {
  def bar[T](c: Class[T]): T = c.newInstance.asInstanceOf[T] // c gotta have a default constructor
}

val fs = new FooString
println(fs.bar(classOf[String]).getClass) // prints "class java.lang.String"
println(fs.bar(classOf[java.util.Date]).getClass) // prints "class java.util.Date"
Walter Chang
Actually, it doesn't works as you expected.The "String" in method FooString#bar is not the type java.lang.String but a type parameter as "T" in Foo.So you may write something like fs.bar(classOf[Int]) and compile OK,but run with an exception.
Eastsun
+4  A: 

It does not work because you do not implements the interface properly. The siganture of your method n scala must be:

def bar[T](c:Class[T]):T

You can fix the behaviour for String only if you want ton implements Foo.

Third try, according to our discussion:

def bar[T](c:Class[T]):T = {
  // Some stuff
  val o:Any = myUntypedFunction(env)
  c.cast(o)
}

According to the context the myUntypedFunction will create an object, and then you use the class parameter to cast your result and obtain a T object. Once again: this behavior is the same in java and in scala.

Nicolas
That's my question: how do I make the signature match?
shaolang
You cannot. You try to change the interface, it's not a scala issue, it's a programming one. the interface says: "Accept any class and return a resut of this class", your implementation says "Accept any String class and returns a string".An implementation of bar must certainly use cast(), newInstance or a constructor to build a T from the object c.
Nicolas
This is only a contrived example; the actual problem I'm facing requires me to implement a Java interface which contains parametrized type in the method, something similar to the example I've given.If there is no way to achieve this, even though this may be an edge case, then it is a scala issue because it is not fully compatible with Java.
shaolang
I don't think that it is a scala issue. You can't do this in Java any way.
Eastsun
@shaolang: If you think it's a scala problem, could you please give us the valid java equivalent code for your FooString class? I still believe you have miss the point of the generics declaration in the bar function.
Nicolas
Yes, this can be done in Java. Seehttp://wicket.apache.org/docs/1.4/org/apache/wicket/Application.html#getMetaData(org.apache.wicket.MetaDataKey) and http://tapestry.apache.org/tapestry5.1/apidocs/org/apache/tapestry5/ioc/ObjectProvider.html#provide(java.lang.Class,%20org.apache.tapestry5.ioc.AnnotationProvider,%20org.apache.tapestry5.ioc.ObjectLocator)
shaolang
We don't say that you can't have this method signature, we explain you that you don't implement it correctly: your implementation should be as incorrect in java than in scala. Once again: give us the corresponding code in java and check if it compiles!
Nicolas
@Nicolas, I really appreciate the thought you have put in to my question. Anyway, the API I need to implement is Tapestry's ObjectProvider interface, which sports similar method signature to my contrived example. I've provided the link in my comment earlier.Since I cannot change the 3rd party API, I have to know how to implement it in Scala.
shaolang
I tried to propose something, I still believe that you have misunderstood the behaviour of the bar function.
Nicolas
@Nicolas, the behavior of the bar function is to return a sub-class of something. Supposed I change the type parameter from String to Comparable, does that make sense to you? The 3rd party API I want to implement allows me to return a sub-class of type T, depending on the some settings I have.
shaolang
No, if the purpose is to return a sub-class of Something, the signature should be <T extends Something>. String can't be changed to Comparable, neither with my answer nor with your initial proposition. I'll try to give you a more complete answer.
Nicolas