Reputation: 5534
Consider the following:
public class GenericTest {
static void print(int x) {
System.out.println("Int: " + x);
}
static void print(String x) {
System.out.println("String: " + x);
}
static void print(Object x) {
System.out.println("Object: " + x);
}
static <T> void printWithClass(T t) {
print(t);
}
public static void main(String argsp[]) {
printWithClass("abc");
}
}
It prints Object: abc. Why doesn't it print String: abc?
Upvotes: 10
Views: 1330
Reputation: 7924
It's not about type erasure, it's a compilation issue and the same thing would happen if the JVM stored method generics at runtime. It's also not about type inference -- the compiler infers <String>
as you would expect.
The issue is that when the compiler is generating code for printWithClass
, it needs a specific method signature to associate with the print
call. Java has no multiple dispatch, so it can't put a vague signature in the method table and decide what to invoke at runtime. The only upper bound on T
is Object
, so the only method that matches is print(Object)
.
Upvotes: 3
Reputation: 3976
Java supports method overriding (dynamic type binding), but not what you are trying to achieve (overloading is static polymorphism and not dynamic).
In order to achieve what you want to achieve in Java, you need double dispatch.
Visitor Pattern should be your friend here.
I have written you a code sample.
public class Test {
public static void main(String argsp[]) {
PrintTypeImpl typeImpl = new PrintTypeImpl(new StringType(), new IntType(), new ObjectType());
typeImpl.accept(new PrintVisitor());
}
static final class PrintVisitor implements TypeVisitor {
public void visit(IntType x) {
System.out.println("Int: ");
}
public void visit(StringType x) {
System.out.println("String: ");
}
public void visit(ObjectType x) {
System.out.println("Object: ");
}
}
interface TypeVisitor {
void visit(IntType i);
void visit(StringType str);
void visit(ObjectType obj);
}
interface PrintType {
void accept(TypeVisitor visitor);
}
static class StringType implements PrintType {
@Override
public void accept(TypeVisitor visitor) {
visitor.visit(this);
}
}
static class ObjectType implements PrintType {
@Override
public void accept(TypeVisitor visitor) {
visitor.visit(this);
}
}
static class IntType implements PrintType {
@Override
public void accept(TypeVisitor visitor) {
visitor.visit(this);
}
}
static final class PrintTypeImpl implements PrintType {
PrintType[] type;
private PrintTypeImpl(PrintType... types) {
type = types;
}
@Override
public void accept(TypeVisitor visitor) {
for (int i = 0; i < type.length; i++) {
type[i].accept(visitor);
}
}
}
}
Upvotes: 4
Reputation: 16209
Extra example to clarify:
public class OverloadingWithGenerics {
static void print(Integer x) {
System.out.println("Integer: " + x);
}
static void print(Double x) {
System.out.println("Double: " + x);
}
static void print(Number x) {
System.out.println("Number: " + x);
}
static <T extends Number> void printWithClass(T t) {
print(t);
}
public static void main(String argsp[]) {
printWithClass(new Integer(1234));
}
}
This prints:
Number: 1234
Upvotes: 0
Reputation: 17893
Generics are interpreted by the compiler and they enforce additional type check to avoid any runtime casting issues. The Generic Type information is lost at runtime. So at runtime what printWithClass receives is just object and not String and hence your result.
Upvotes: 0
Reputation:
static <T> void printWithClass(T t) {
print(t);
}
will be comipled to
static void printWithClass(Object t) {
print(t);
}
Upvotes: 0
Reputation: 726509
This is because of Java type erasure: your
static <T> void printWithClass(T t) {
print(t);
}
is actually a syntactic sugar on top of
static void printWithClass(Object t) {
print(t);
}
To be fair, that "syntactic sugar" lets the compiler do some very nice and important checking, but at run-time there is only one copy of the printWithClass
method, and it uses java.lang.Object
as the type of your variable t
.
If you have experienced generics in other languages (C#, C++ templates, Ada) type erasure would come in contrast to what you know, but this is how it works under the cover.
Upvotes: 10
Reputation: 164129
Because it can know that only on run time, but in reality, since java is a compiled language and not a scripted one, it's being decided on compilation time.
The java generics allow "a type or method to operate on objects of various types while providing compile-time type safety."
You can of course try something like:
static <T extends String> void printWithClass(T t) {
print(t);
}
though it's not what you're after, which is not possible since the compiler is calling the shots.
Upvotes: 0
Reputation: 5744
Because java generics aren't generics the way you think they are. When generic java code gets compiled all type information actually gets stripped away, and only the base known type remains. In this case that type is Object
.
Generics in java is really only compiler trickery, where the compiler removes the casts that would otherwise be necessary and induces a compile time restraint. In the end, all that is left is the base type when this actually gets compiled into byte code.
This process is called type erasure. This previous question is helpful to understand what actually goes on.
Upvotes: 0