Skip to content

Access of static class method by static generic method #34131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
artembatura opened this issue Aug 11, 2018 · 4 comments
Closed

Access of static class method by static generic method #34131

artembatura opened this issue Aug 11, 2018 · 4 comments
Labels
closed-as-intended Closed as the reported issue is expected behavior

Comments

@artembatura
Copy link

I try call static method of class by generic method and it's not compiling with error:
Error: The method 'foo' isn't defined for the class 'dart.core::Type'

https://dartpad.dartlang.org/a94baf3ef84a6b22827a39aa4c5133b8

class A {
  static void foo() {
    print('I <3 ');
  }
}

class B {
  static void bar<T extends A>() {
    T.foo();
    print('Dart');
  }
}

void main() {
   B.bar<A>();
}

PS: I doesn't know, it's an issue, bug or only question, but I would be glad if it was possible

And it's normally work in Java!
public class A {
  public static void foo() {
    System.out.print("I <3 ");
  }
}

public class B {
  public static <T extends A> void bar() {
    T.foo();
    System.out.print("Java");
  }
}

public class Main {
  public static void main(String[] args) {
     B.<A>bar();
  }
}
@matanlurey
Copy link
Contributor

It is not possible in Dart to call static methods this way.

@matanlurey matanlurey added the closed-as-intended Closed as the reported issue is expected behavior label Aug 11, 2018
@eernstg
Copy link
Member

eernstg commented Aug 13, 2018

Hi @artemirq, @matanlurey already mentioned that Dart static methods cannot be called as shown. However, I'm tempted to add a few more words on that.

First, it doesn't actually work in Java:

// Cf. SDK issue #34131

class A {
  public static void foo() {
    System.out.print("I <3 ");
  }
}

class A2 extends A {
  public static void foo() {
    System.out.print("Let's see whether B also <3 ");
  }
}

class B {
  public static <T extends A> void bar() {
    T.foo();
    System.out.print("Java");
  }
}

public class n000 {
  public static void main(String[] args) {
     B.<A>bar(); // Prints 'I <3 Java'
     B.<A2>bar(); // Also prints 'I <3 Java'
  }
}

With javap you can see that it's generating code for an invocation of A.foo, and it doesn't matter that T can have other values than A for some invocations of bar:

class B {
  B();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static <T extends A> void bar();
    Code:
       0: invokestatic  #2                  // Method A.foo:()V
       3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       6: ldc           #4                  // String Java
       8: invokevirtual #5                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
      11: return
}

But that's not surprising—Java must ignore the actual value of T, because Java performs type argument erasure during compilation, that is, there is nothing around at run time which could be used to select A.foo for the first invocation and A2.foo for the second one.

Dart has reified type arguments, so T is actually around at run time, and it wouldn't be particularly hard to support that T.foo() must call A.foo() when T has the value A, and A2.foo() when T is A2.

The main reason why we did not do it is that there is no typing relation between the static methods of any two classes, and it makes no difference whether those two classes are subclasses or subtypes of each other—each class has its own set of static methods, and there is simply no relationship between those sets of methods.

So even if we did give you the ability to do T.foo() where T is a type variable where we only know that T extends A, we would have no idea whether that T actually has a static method named foo, and, if so, whether it would accept the empty argument list.

So what you are asking for does not match up with the more strict typing that Dart uses today (especially Dart 2 which was released recently, but we've been moving in that direction for a couple of years before that).

Returning to the actual error that you're seeing: T evaluates to an instance of a class Type which is used to identify types at run-time. So you can do print(int) and it will print "int", and if you do print(T) in a situation where T is a type variable whose value is int then it will also print "int". But those Type objects are not intended to do much else than allowing you to recognize a type (that is, T == int can be used to test whether T has the value int).

@artembatura
Copy link
Author

@eernstg Thank you very much for your research and detailed answer. I agree, perhaps in real-world problems call static methods by this way really not needed

By the way, I tried DLang and your example works fine in this lang (unlike Java)

import std.stdio;

class A {
    static foo() {
    	writeln("A.foo");
    }
}

class A2 : A {
    static foo() {
    	writeln("A2.foo");
    }
}

class B {
    static foobar(T : A)() {
        T.foo();
    }
}

void main() {
    B.foobar!A();
    B.foobar!A2();
}

Playground https://run.dlang.io

@eernstg
Copy link
Member

eernstg commented Aug 15, 2018

Interesting! I had to find a bit of time to look into D in order to understand what on earth that piece of code is doing. ;-)

However, it looks like foobar is a template and the two invocations will actually be resolved at compile-time, such that the calls to T.foo() will work just like two separate pieces of code where T is replaced by A in the first case and by A2 in the second: We have the limitation 'Templates cannot be used to add non-static fields or virtual functions to classes or interfaces' here, which seems to fit that assumption.

In Dart, we could add an instance method to the Type corresponding to a given class C for each static method declared in C (it wouldn't be hard, but it would not fit so well in a typed setting), and with that we would be able to do the same thing at run time (which I suspect that D cannot do, it can only do something that corresponds to a compile-time macro expansion).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-as-intended Closed as the reported issue is expected behavior
Projects
None yet
Development

No branches or pull requests

3 participants