Generic methods and method overloading
Are the two methods overloaded?
Yes.
Shouldn't statement
A<int>.MyMethod(myInt);
throw an error, since constructed typeA<int>
has two methods with the same signature?
The question doesn't make sense; A
is not a generic type as you have declared it. Perhaps you meant to ask:
Should the statement
A.MyMethod(myInt);
cause the compiler to report an error, since there are two ambiguous candidate methods?
No. As others have said, overload resolution prefers the non-generic version in this case. See below for more details.
Or perhaps you meant to ask:
Should the declaration of type A be illegal in the first place, since in some sense it has two methods with the same signature,
MyMethod
andMyMethod<int>
?
No. The type A is perfectly legal. The generic arity is part of the signature. So there are not two methods with the same signature because the first has generic arity zero, the second has generic arity one.
Or perhaps you meant to ask:
class G<T>
{
public static void M(T t) {}
public static void M(int t) {}
}
Generic type
G<T>
can be constructed such that it has two methods with the same signature. Is it legal to declare such a type?
Yes, it is legal to declare such a type. It is usually a bad idea, but it is legal.
You might then retort:
But my copy of the C# 2.0 specification as published by Addison-Wesley states on page 479 "Two function members declared with the same names ... must have have parameter types such that no closed constructed type could have two members with the same name and signature." What's up with that?
When C# 2.0 was originally designed that was the plan. However, then the designers realized that this desirable pattern would be made illegal:
class C<T>
{
public C(T t) { ... } // Create a C<T> from a given T
public C(Stream s) { ... } // Deserialize a C<T> from disk
}
And now we say sorry buddy, because you could say C<Stream>
, causing two constructors to unify, the whole class is illegal. That would be unfortunate. Obviously it is unlikely that anyone will ever construct this thing with Stream as the type parameter!
Unfortunately, the spec went to press before the text was updated to the final version. The rule on page 479 is not what we implemented.
Continuing to pose some more questions on your behalf:
So what happens if you call
G<int>.M(123)
or, in the original example, if you callA.MyMethod(123)
?
When overload resolution is faced with two methods that have identical signatures due to generic construction then the one that is generic construction is considered to be "less specific" than the one that is "natural". A less specific method loses to a more specific method.
So why is it a bad idea, if overload resolution works?
The situation with A.MyMethod
isn't too bad; it is usually pretty easy to unambiguously work out which method is intended. But the situation with G<int>.M(123)
is far worse. The CLR rules make this sort of situation "implementation defined behaviour" and therefore any old thing can happen. Technically, the CLR could refuse to verify a program that constructs type G<int>
. Or it could crash. In point of fact it does neither; it does the best it can with the bad situation.
Are there any examples of this sort of type construction causing truly implementation-defined behaviour?
Yes. See these articles for details:
https://ericlippert.com/2006/04/05/odious-ambiguous-overloads-part-one/
https://ericlippert.com/2006/04/06/odious-ambiguous-overloads-part-two/
Overloading Methods in Java with generics
You cannot have two methods with the same erasure in the same type. That said, you may be able to manipulate the erasure retaining the same type. For instance, if Foo
was an interface:
interface Foo<T> { /* ... */ }
class Bar {
public void do_(Foo<Integer> obj) { }
public <A extends Object & Foo<Double>> void do_(A obj) { }
}
But, avoid overloading methods even where generics are not involved.
Java overloading with generic type parameter
Overloading is resolved at compile time, so you can't rely on the runtime types.
What you can do is check the runtime types with instanceof
and cast to the relevant type explicitly:
void delete(List<? extends Object> list){
for (Object o: list) {
if (o instanceof ClassA) {
delete((ClassA) o);
} else if (o instanceof ClassB) {
delete((ClassB) o);
}
}
}
Using a generic parameter on overloaded methods
Overload resolution is only done at compile-time.
Since GenericFunc<T>
doesn't know whether T
is a string
or something else at compile-time, it can only ever use the Print<T>(T value)
"overload".
Using dynamic
, you can change this to a dynamic dispatch, and get the behaviour you expect:
Print((dynamic)value);
This makes the overload resolution happen at runtime, with the actual runtime type of value
.
Overloading method with different parameterized type parameter
Java has this thing called "type erasure" which causes problems like this when compiling generic code like this. When compiled, List<Number>
and List<Integer>
are both just plain List
.
Java method overloading - Generic parameter & parameters within same inheritance tree
The rules of determining which method signature to call at compile-time are explained in the language specification. Specifically important is the section on choosing the most specific method. Here are the parts related to your questions:
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.
...
One applicable method
m1
is more specific than another applicable methodm2
, for an invocation with argument expressionse1
, ...,ek
, if any of the following are true:
m2
is generic, andm1
is inferred to be more specific thanm2
for argument expressionse1
, ...,ek
by §18.5.4.
m2
is not generic, andm1
andm2
are applicable by strict or loose invocation, and wherem1
has formal parameter types S1, ..., Sn andm2
has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argumentei
for all i (1 ≤ i ≤ n, n = k)....
A type S is more specific than a type T for any expression if S <: T (§4.10).
In this case, Integer
is more specific than Number
because Integer
extends Number
, so whenever the compiler detects a call to foo
that takes a variable declared of type Integer
, it will add an invocation for foo(Integer)
.
More about the first condition related to the second method being generic is explained in this section. It's a little verbose but I think the important part is:
When testing that one applicable method is more specific than another (§15.12.2.5), where the second method is generic, it is necessary to test whether some instantiation of the second method's type parameters can be inferred to make the first method more specific than the second.
...
Let
m1
be the first method andm2
be the second method. Wherem2
has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp]....
The process to determine if
m1
is more specific thanm2
is as follows:...
If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)
Which basically means that foo(Number)
and foo(Integer)
are both more specific than foo(T)
because the compiler can infer at least one type for the generic method (e.g. Number
itself) that makes foo(Number)
and foo(Integer)
more specific (this is because Integer <: Number
and Number <: Number
) .
This also means that in your code foo(T)
is only applicable (and inherently the most specific method since it's only the one applicable) for the invocation that passes a String
.
Overload method with generic method and parameter
You don't need to overload the method because Java does this implicitly for you with raw types.
<E> E[] toArray(List<E> data) {
If you call
List objs = ...
Object[] array = toArray(objs);
and if you use generics you get generics
List<String> strs =
String[] array = toArray(objs);
The real problem you have is there is no way to implement toArray. This is because the generic type is not known at at runtime. i.e. you can't create an array of E
nor can you cast an Object[]
to a String[]
for example.
What you can do is pass the type of the array to use.
public static Object[] toArray(List list) {
return toArray(list, Object.class);
}
public static <E> E[] toArray(List<? extends E> list, Class<E> eClass) {
@SuppressWarnings("unchecked")
E[] array = (E[]) Array.newInstance(eClass, list.size());
for (int i = 0; i < array.length;i++)
array[i] = list.get(i);
return array;
}
Related Topics
Get List<> Element Position in C# Using Linq
How to Register Windows Forms with Simple Injector
Ef4 Cause Circular Reference in Web Service
Gridview Bound with Properties of Nested Class
How to Unescape and Reescape Strings in .Net
How to Have an Optional Parameter for an ASP.NET Soap Web Service
How to Use Extension Methods and Linq in .Net 2.0 or 3.0
What Thread Runs the Code After the 'Await' Keyword
How to Get Full Host Name + Port Number in Application_Start of Global.Aspx
Dynamically Cross-Join Multiple Different-Size Collections Together in Linq (C#)
How to Use Xpath Function in a Xpathexpression Instance Programatically
"Do Not Use Abstract Base Class in Design; But in Modeling/Analysis"
What Does the Tilde Mean in an Expression