2012-02-16 3 views
11

Рассмотрим следующий пример:Java генерики - тип выводя

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"); 
    } 
} 

Он печатает объекта: ABC. Почему он не печатает String: abc?

+0

увидеть этот вопрос о получении типа Т - [Get Generic Тип класса во время выполнения] (HTTP: // StackOverflow .com/questions/3403909/get-generic-type-of-class-at-runtime) – csturtz

ответ

4

Java поддерживает метод переопределения (привязка динамического типа), но не то, что вы пытаетесь достичь (перегрузка - это статический полиморфизм, а не динамический).

Для того, чтобы достичь того, чего вы хотите достичь на Java, вам потребуется двойная отправка.

Шаблон посетителя должен быть вашим другом здесь.

Я написал вам образец кода.

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); 
      } 
     } 
    } 

} 
0

Поскольку дженерики java не являются дженериками, как вы думаете. Когда обобщенный код Java компилируется, вся информация о типе фактически удаляется, и остается только базовый известный тип. В этом случае этот тип равен Object.

Дженерики в java - это действительно только хитрость компилятора, где компилятор удаляет отливки, которые в противном случае были бы необходимы, и вызывая ограничение времени компиляции. В конце концов, все, что осталось, является базовым типом, когда он фактически скомпилируется в байтовый код.

Этот процесс называется тип erasure. Этот previous question помогает понять, что на самом деле происходит.

0

Потому что он может знать, что только во время выполнения, но на самом деле, поскольку java - это скомпилированный язык, а не сценарий, он определяется во время компиляции.

java generics позволяют использовать тип или метод для объектов различного типа, предоставляя время компиляции типа безопасности. "

Вы можете, конечно, попробовать что-то вроде:

static <T extends String> void printWithClass(T t) { 
    print(t); 
} 

, хотя это не то, что вы после этого, что невозможно, так как компилятор называет выстрелы.

10

Это из-за Java type erasure: Ваш

static <T> void printWithClass(T t) { 
    print(t); 
} 

фактически синтаксический сахар поверх

static void printWithClass(Object t) { 
    print(t); 
} 

Чтобы быть справедливым, что «синтаксический сахар» позволяет компилятору сделать некоторые очень хорошие и важная проверка, но во время выполнения существует только одна копия метода printWithClass, и она использует java.lang.Object как тип вашей переменной t.

Если у вас есть опытные генераторы на других языках (стили C#, C++ templates, Ada), то это будет контрастировать с тем, что вы знаете, но так оно и работает под обложкой.

+0

Речь идет не о стирании стилей, а о общих ограничениях, которые доступны _at compile time_. Если мы используем '', вывод будет 'String: abc'. –

+1

@ Даниэль абсолютно - потому что тогда метод стал бы «static void printWithClass (String t)». Но он остался бы единственным методом с одним типом, связанным во время компиляции, чтобы вызвать одиночную перегрузку статического метода 'print'. – dasblinkenlight

0
static <T> void printWithClass(T t) { 
    print(t); 
} 

будет comipled к

static void printWithClass(Object t) { 
    print(t); 
} 
0

Дженерики интерпретируются компилятором, и они применять дополнительную проверку типа, чтобы избежать каких-либо проблем литья во время выполнения. Общая информация типа - lost at runtime. Поэтому во время выполнения, который получает printWithClass, это просто объект, а не String и, следовательно, ваш результат.

4

Речь идет не о стирании стилей, это проблема компиляции, и то же самое произойдет, если JVM хранит обобщенные методы во время выполнения. Это также не касается вывода типа - компилятор сообщает <String>, как и следовало ожидать.

Проблема заключается в том, что когда компилятор генерирует код для printWithClass, ему нужна специальная сигнатура метода для связи с вызовом print. Java не имеет множественной отправки, поэтому не может поставить неопределенную подпись в таблице методов и решить, что вызывать во время выполнения. Единственная верхняя граница на T - Object, поэтому единственным методом, который соответствует print(Object).

0

Дополнительный пример для уточнения:

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)); 
    } 
} 

Это печатает:

Number: 1234 
Смежные вопросы