Scala Programming Language
  1. Scala Programming Language
  2. SI-1737

Cannot Implement Method of Java Interface with Type Parameter Omitted by It's Sub-Class

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Misc Compiler
    • Labels:
      None

      Description

      Minim case:

      There is a Java interface A:

      public interface A<T extends String> {
      void run(T t);
      }

      Which has a type parameter <T extends String> and abstract method run(T t)

      And a Java abstract class B with is sub-class of A. But, B, as it,
      omitted type parameter of A. This is unsafe but valid in Java:

      public abstract class B implements A {
      public String me()

      { return "I'm B"; }

      }

      Assume above class A and B have been compiled under javac, and packed
      as jar library, and I can not patch it anymore. Now I need to write a
      class S in Scala which extends B:

      class S extends B {
      override
      def run[T <: String](t:T) =

      {println(t)}
      }

      scalac complained as:
      /Users/dcaoyuan/NetBeansProjects/ScalaTestCase/src/S.scala:1: error:
      class S needs to be abstract, since method run in trait A of type
      (T)Unit is not defined
      class S extends B {
      /Users/dcaoyuan/NetBeansProjects/ScalaTestCase/src/S.scala:3: error:
      method run overrides nothing
      def run[T <: String](t:T) = {println(t)}

      I than tried "forSome" type:

      class S extends B {
      override
      def run(t:T forSome

      {type T <: String}

      ) =

      {println(t)}
      }

      Again, I got:
      /Users/dcaoyuan/NetBeansProjects/ScalaTestCase/src/S.scala:1: error:
      class S needs to be abstract, since method run in trait A of type
      (T)Unit is not defined
      class S extends B {
      /Users/dcaoyuan/NetBeansProjects/ScalaTestCase/src/S.scala:3: error:
      method run overrides nothing
      def run(t:T forSome {type T <: String}) = {println(t)}

      It seems that, since B omitted A's type parameter T, I have no way to
      get what is T, and can not successfully override/implement "run(T t)"
      method.

      I tried other forms of "forSome" usages, and always failed.

        Activity

        Hide
        Lukas Rytz added a comment -

        Is it possible to implement class `S` in Java? What would you use as parameter type for method `run`?

        Show
        Lukas Rytz added a comment - Is it possible to implement class `S` in Java? What would you use as parameter type for method `run`?
        Hide
        Caoyuan Deng added a comment -

        Yes, it can be implemented in Java, use "String" is enough:

        public class JS extends B {
        public void run(String t)

        { System.out.println(t); }

        }

        Show
        Caoyuan Deng added a comment - Yes, it can be implemented in Java, use "String" is enough: public class JS extends B { public void run(String t) { System.out.println(t); } }
        Hide
        Martin Odersky added a comment -

        Scala does not understand raw types. This is some of the ``baggage_ that we dropped from Java. When Scala sees a raw type it treats it as an existential. Unfortunately, that means that in the case above class B extends A with some unknown type parameter T. Since T is unknown, we can't implement a run method with this type. So we are stuck.

        The only thing one can do in these situations is implement a subclass in Java that implements the method. I'm afraid there's nothing else we can do without introducing raw types into Scala. And the latter we want to avoid.

        Show
        Martin Odersky added a comment - Scala does not understand raw types. This is some of the ``baggage_ that we dropped from Java. When Scala sees a raw type it treats it as an existential. Unfortunately, that means that in the case above class B extends A with some unknown type parameter T. Since T is unknown, we can't implement a run method with this type. So we are stuck. The only thing one can do in these situations is implement a subclass in Java that implements the method. I'm afraid there's nothing else we can do without introducing raw types into Scala. And the latter we want to avoid.
        Hide
        Paul Phillips added a comment -

        If we can reliably recognize these situations, I think it would be hugely helpful for the error message to say something to the effect that the class must be implemented in java. In this example, when scala loads abstract class B, it ought to be able to determine that it's impossible to implement it scala, right? So once a class declares that it extends B it would generate that error.

        If that sounds plausible to you I'd like to reopen this ticket as an enhancement.

        Show
        Paul Phillips added a comment - If we can reliably recognize these situations, I think it would be hugely helpful for the error message to say something to the effect that the class must be implemented in java. In this example, when scala loads abstract class B, it ought to be able to determine that it's impossible to implement it scala, right? So once a class declares that it extends B it would generate that error. If that sounds plausible to you I'd like to reopen this ticket as an enhancement.
        Hide
        michid4 added a comment -

        There seems to be a workaround for the simpler case:

        public interface A<T extends String> {
            T get();
        }
        
        public abstract class B implements A {}
        
        class C extends B {
            this: A[_] =>
            def get = "foo"
        }
        

        See http://michid.wordpress.com/2009/06/24/puzzle-implement-this-solution/

        If we could actually refer to the existential type used in the self type, this could probably also be used to work around the original issue.

        Show
        michid4 added a comment - There seems to be a workaround for the simpler case: public interface A<T extends String> { T get(); } public abstract class B implements A {} class C extends B { this: A[_] => def get = "foo" } See http://michid.wordpress.com/2009/06/24/puzzle-implement-this-solution/ If we could actually refer to the existential type used in the self type, this could probably also be used to work around the original issue.
        Hide
        Adriaan Moors added a comment -

        fails in r18657 with cyclic reference error:

        error: illegal cyclic reference involving type _$$1
                   this: A[_] =>
                               ^
        
        Show
        Adriaan Moors added a comment - fails in r18657 with cyclic reference error: error: illegal cyclic reference involving type _$$1 this: A[_] => ^
        Hide
        Antonio Cunei added a comment -

        Reassigning to Adriaan in the meantime, we can discuss it further in two weeks.

        Show
        Antonio Cunei added a comment - Reassigning to Adriaan in the meantime, we can discuss it further in two weeks.
        Hide
        Paul Phillips added a comment -

        (In r20559) Test case closes SI-1737. Review by community.

        Show
        Paul Phillips added a comment - (In r20559) Test case closes SI-1737 . Review by community.

          People

          • Assignee:
            Adriaan Moors
            Reporter:
            Caoyuan Deng
            TracCC:
            Michael Nascimento Santos, Paul Phillips
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development