participate


Generics - overloading of generic methods
<<   Back to Forum  |   Give us Feedback
This topic has 13 replies on 1 page.
langer
Posts:113
Registered: 6/12/97
overloading of generic methods   
Jul 29, 2003 4:55 AM

 
I'm not sure that I understand overloading of generic methods.

The spec says in section 5.6.1, page 15:

"Overload resolution changes in several ways due to genericity and covariant return types. In particular: The existing specification assumes that if there are multiple candidate methods with the same signature, they all have the same return type. This is no longer the case.
...
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen."

Why is the following illegal then?
public class Test {
	private static class someClass implements Cloneable {
	}
 
	private static <T> T f() {
		return null;
	}
	private static <T extends someClass> T f() {
		return null;
	}
   	private static <T extends Cloneable> T f() {
		return null;
	}
 
	private static void test1() {
		someClass d = null;
		String s = null;
		Cloneable c = null;
		
		d = f();
		s = f(); 
		c = f();
		f();
	}
	public static void main(String[] args) {
		test1();
	}
}


The compiler complains about ambiguous method calls when I invoke f(), no matter how I invoke it. Why doesn't it pick the most specific method?


In the example of
 someClass d = f(); 
the compiler must infer the type parameter, which in this case would be T:=someClass. And then I would think that
 <T extends someClass> T f()  
is the most specific method.

The spec describes what "more specific" means, but only for methods that have arguments, as far as I understand it. What does it mean for methods without arguments?


In the example of
 String s = f(); 
the compiler would infer T:=String. And then there is only one viable candidate, namely
 <T> T f()  
. The compiler does not even have to decide which candidate is the most specific one, and still it complains about an ambiguity. Why?

I would perhaps understand it if the compiler rejected the method definitions as duplicates in the first place, because you cannot overload methods that only differ in their return types. But that's not what it does.


I'm sure I'm missing something essential here. What is it? Does anybody have any idea?
 
gafter
Posts:669
Registered: 6/25/98
Re: overloading of generic methods   
Jul 30, 2003 11:09 PM (reply 1 of 13)  (In reply to original post )

 
Inference of the type parameter occurs AFTER selecing the method, not before.
 
langer
Posts:113
Registered: 6/12/97
Re: overloading of generic methods   
Jul 30, 2003 11:58 PM (reply 2 of 13)  (In reply to #1 )

 
Oh, I see. Does the spec say so?

Thanks for the clarification.
 
gafter
Posts:669
Registered: 6/25/98
Re: overloading of generic methods   
Jul 31, 2003 3:44 PM (reply 3 of 13)  (In reply to #2 )

 
Oh, I see. Does the spec say so?

Perhaps that should be made more explicit than simply ordering the sections of the spec.
 
brucechapman
Posts:683
Registered: 11/24/98
Re: overloading of generic methods   
Jul 31, 2003 7:51 PM (reply 4 of 13)  (In reply to #3 )

 
Perhaps...


Understatement of the year award, any other nominations ??
 
langer
Posts:113
Registered: 6/12/97
Re: overloading of generic methods   
Aug 1, 2003 1:30 AM (reply 5 of 13)  (In reply to #3 )

 
Oh, I see. Does the spec say so?

Perhaps that should be made more explicit than simply
ordering the sections of the spec.


There is another detail missing in the spec: does the compiler, during the type inference phase, infer types that conform to the bounds? or, does it infer types (regardless of the bounds) and checks against the bounds afterwards? It makes a difference ...
 
Kraythe
Posts:458
Registered: 7/1/03
Re: overloading of generic methods   
Aug 7, 2003 8:18 AM (reply 6 of 13)  (In reply to #5 )

 
The reality is that your original statement had nothing to do with generics specifically. You cannot differentiate methods by return type, only by parameters. Therefore your example rewritten as the following would still fail in compilation:


public class foo {
  A f() {...}
  B f() {...}
  C f() {...}
}


Since the compiler for templates would boil your example down to that, you would get an ambiguity in the same manner as if you wrote that without templates.
 
langer
Posts:113
Registered: 6/12/97
Re: overloading of generic methods   
Aug 10, 2003 11:49 PM (reply 7 of 13)  (In reply to #6 )

 
The reality is that your original statement had
nothing to do with generics specifically. You cannot
differentiate methods by return type, only by
parameters.

This is true for regular non-generic methods, but not for parameterized methods.


The specification says (page 9, section 3.1):

"It is illegal to declare two methods with the same name and the same argument types in a class. The definition of "having the same argument types" is extended to generic methods as follows:

Two method declarations M and N have the same arguments types if either none of them has type parameters and their argument types agree, of they have the same number of type parameters, say <A1,...,An> for M and <B1,...,Bn> for N, and after renaming each occurence of a Bi in N's type to Ai the bounds of corresponding type variable are the same and the arguments tpyes of M and N agree."


In my examples the parameterized methods have different bounds, which means, that under the definition in the specification they do NOT "have the same argument types". Hence it is expressly allowed to declare these methods in the same class.

	private static <T> T f() {
		return null;
	}
	private static <T extends someClass> T f() {
		return null;
	}
   	private static <T extends Cloneable> T f() {
		return null;
	}
 
gafter
Posts:669
Registered: 6/25/98
Re: overloading of generic methods   
Aug 11, 2003 12:31 PM (reply 8 of 13)  (In reply to #7 )

 
In my examples the parameterized methods have
different bounds, which means, that under the
definition in the specification they do NOT "have the
same argument types". Hence it is expressly allowed
to declare these methods in the same class.

Not disallowed by that section of the spec, agreed. Disallowed by the constraints in section 6.2 on the bottom of page 19.
 
langer
Posts:113
Registered: 6/12/97
Re: overloading of generic methods   
Aug 11, 2003 11:22 PM (reply 9 of 13)  (In reply to #8 )

 

I guess, you are referring to this section:

"It is a compile time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold:
? m1 and m2 have the same name.
? m2 is accessible from T.
? m1 and m2 have different signatures.
? m1 or some method m1 overrides (directly or indirectly) has the same erasure as m2 or some
method m2 overrides (directly or indirectly)."

My methods have the same name and all three are accessible. But they do NOT have different signatures and the do NOT have the same erasure. Why would they be disallowed?

Is this another specification error? Or am I misreading anything?

What is the signature of a parameterized method? The spec does not define the term "signature" for a generic method. But the JLS has a definition for non-generic methods:

"8.4.2 Method Signature: The signature of a method consists of the name of the method and the number and types of formal parameters to the method. A class may not declare two methods with the same signature, or a compile-time error occurs."

Provided that this definition still holds I would think the signature of all my methods is: "f()" [ Is the type argument list part of the signature of a generic method? In that case, my methods would indeed have different signatures. ]

What is the erasure of a parameterized method? The spec does not explicitly define the term "erasure" for a generic method, but I guess the beginning of section 6.2 is supposed to serve as a definition:

"Each method T m(T1, . . . , Tn) throws S1, . . . , Sm is translated to a method with the same name whose return type, argument types, and thrown types are the erasures of the corresponding types in the original method."

Seemingly the return type is part of the erasure. According to that definition the three method have different erasures:

Object f()
someClass f()
Cloneable f()


Also, there is section 5.6.1:

"Overload resolution changes in several ways due to genericity and covariant return types. In particular:
? The existing specification assumes that if there are multiple candidate methods with the same signature, they all have the same return type. This is no longer the case.
? It is possible that a particular instantiation would have several concrete methods with the same signature. This was not possible before."

I don't know, but I read this as: yes, you can have several methods with the same signature (as defined in the JLS) and different return types.



I think I need a clarification ... please.
 
Kraythe
Posts:458
Registered: 7/1/03
Re: overloading of generic methods   
Aug 12, 2003 5:12 AM (reply 10 of 13)  (In reply to #9 )

 
OK, I will try to be specific. The definition of generics does not change the fact that you have several methods whose signature is identical. The return type of a method is NOT a component of its signature; therefore, the return type can not be used to select a method, nor would it be feasible to do so. In order to accomplish that, the language would have to inspect the context in which a method is called and take appropriate action based upon that context. In other words, selection of the behavior on the right hand of an assignment would depend on the contents of the left hand of the assignment.

In a simple case such as the following code, the question is easy:
Integer x = foo.f();

In this case we merely look at the left side and see that it wants an Integer and go with the method that returns an integer. Similar results would occur if x was of type Object or Number.

However, it gets more complex with nested expressions.
Integer x = someObj.g(someObj.y * Math.abs(someObj.floatValue() * 
            anotherObj.g(foo.f()));


Now we have a royal mess to decode. Assuming the worst case where each of the objects has similar overloaded methods with varying return types but the same signature as well as overloaded methods with the varying signatures, we now have to decode the context in which the method is called to know which method to call. In the case of foo, the f() method call will depend upon the needs of anotherObj.g(); however if g() has methods to accept an Integer or a Number type, then things get ambiguous because f() has methods that produce an Integer and one that produces a Number. What is more, you cant count on the g() that takes an Integer as being fundamentally the same as the g() that takes a Number. So now which one do you pick? Furthermore if both g() methods that take an Integer and one that takes a Number are further overloaded by return type then the mess propagates into the call chain like a cancer. Even if you could get it to work, it would run like a three legged dog in quicksand.

The only legitimate solution is to define methods that overload based upon parameter type. In this manner the indications are clear as to which method to select. And thus all overloadable languages select by parameter instead of return type.

Back to your question, you are attempting to overload by return type. Dont let the extra keywords and angle brackets confuse you. The parameters are identical. In fact, the compiler will boil out all the extra stuff and end up with A, B, C type classes as I stated in my example.

Therefore, when you boil it down to the generated byte code, it violates the rules of java and wont run. Once you boil out the extra syntax, anything that wont run in a normal JVM wont run under 1.5. In fact, the stuff for generics ALL happens at compile time and the end result byte code is pre-generics comptible; hence the reason the changes for generics are in the compiler and not the JVM.

Sorry but I cant be any more clear than that.
 
Kraythe
Posts:458
Registered: 7/1/03
Re: overloading of generic methods   
Aug 12, 2003 5:21 AM (reply 11 of 13)  (In reply to #10 )

 

Sample to supplement my reply.

Given:


public class Foo {
 
  public Integer x() { ... }
  public Number x() { ... }
  public Object x() { ... }
}
 
public class Bar {
 
  public void y(final Integer value) { ... }
  public void y(final Number value) {
    ... 
    // very different than first y(Integer and y(Object) 
  }
  public void y(final Object value) { 
    ...
    // very different than y(Integer) and y(Number) method 
  }
}
 


What method is chosen for the following call?

Bar q = new Bar();
Foo r = new Foo();
 
q.y(r.x());
 


See why it wouldnt work?
 
langer
Posts:113
Registered: 6/12/97
Re: overloading of generic methods   
Aug 12, 2003 11:16 PM (reply 12 of 13)  (In reply to #10 )

 

Sorry but I cant be any more clear than that.

Oh, no, your examples illustrate the point very well. Thank you.

The "royal mess" from which the compiler can't do overload resolution is also the reason why method calls are not considered "context" for type argument inference, I guess. But the compiler performs type argument inference when the context is an assignment. Why doesn't it do overload resolution in that case, too? My examples are fairly simple; it's not that the compiler is incapable of figuring out which method I intend to invoke.

Wouldn't it be more consistent and easier to understand if the context for overload resolution were the same as the context for type argument inference? Isn't it a perfectly natural expectation? When a programmer sees that the compiler can figure out the type argument of a parameterized method from its occurrence in an assignment, (s)he might wonder why the compiler can't choose the right method from a set of overloads in the same situation. But that's an aside.


The real question is: what is the spec trying to say? The spec expressly talks of modified overload resolution rules due to genericity (see section 5.6.1) and says: "It is possible that a particular instantiation would have several concrete methods with the same signature. This was not possible before."

I'm not sure what a "concrete" method is. I guess, it is a particular instantiation of a generic method (a generic method being either a parameterized method and/or a non-static method of a parameterized class). My example has several concrete methods with the same signature. Why is it illegal?

I understand your explanations, but I do not understand how they relate to the Generics spec.
 
gafter
Posts:669
Registered: 6/25/98
Re: overloading of generic methods   
Aug 14, 2003 4:42 PM (reply 13 of 13)  (In reply to original post )

 
The compiler complains about ambiguous method calls
when I invoke f(), no matter how I invoke it. Why
doesn't it pick the most specific method?

Because none is most specific.

In the example of
 someClass d = f(); 

the compiler must infer the type parameter, which in
this case would be T:=someClass. And then I would
think that
 <T extends someClass> T f()
  
is the most specific method.

I don't see any way to conclude that from the spec.
There is not concept of "most specific" in the JSR14 spec.
Perhaps you mean maximally specific.

The spec describes what "more specific" means, but
only for methods that have arguments, as far as I
understand it. What does it mean for methods without
arguments?

The definition generalizes to zero parameters without
a problem. In this case each is more specific and strictly
more specific than the other, but none is maximally specific.

In the example of
 String s = f(); 
the
compiler would infer T:=String. And then there is
only one viable candidate, namely
 <T> T f()
. The compiler does not even have to decide
which candidate is the most specific one, and still
it complains about an ambiguity. Why?

Because the test that an inferred type parameter is in
bounds is not part of the "applicable" test. Perhaps it
should be. File a spec bug.
 
This topic has 13 replies on 1 page.
Back to Forum
 
Read the Developer Forums Code of Conduct

Click to email this message Email this Topic

Edit this Topic
  
 
 
Forums Statistics
    Users Online : 26
  • Guests : 129

About Sun forums
  • Sun Forums is a large collection of user generated discussions. It is here to help you ask questions, find answers, and participate in discussions.

    Check out our guide on Getting started with Sun Forums for a full walkthrough of how to best leverage the benefits of this community.

Powered by Jive Forums