Генерирование 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");
}
}
Он печатает Object: abc.
Почему он не печатает String: abc?
Ответы
Ответ 1
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);
}
}
}
}
Ответ 2
Это из-за стирание стилей Java: ваш
static <T> void printWithClass(T t) {
print(t);
}
- фактически синтаксический сахар поверх
static void printWithClass(Object t) {
print(t);
}
Справедливости ради следует, что этот "синтаксический сахар" позволяет компилятору выполнять очень приятную и важную проверку, но во время выполнения существует только одна копия метода printWithClass
, и она использует java.lang.Object
как тип ваша переменная t
.
Если у вас есть опытные образцы в других языках (стирание С#, С++ templates, Ada), это будет контрастировать с тем, что вы знаете, но так оно работает под обложкой.
Ответ 3
Это не о стирании типа, это проблема компиляции, и то же самое произойдет, если JVM хранит универсальные методы во время выполнения. Это также не касается вывода типа - компилятор сообщает <String>
, как и следовало ожидать.
Проблема заключается в том, что когда компилятор генерирует код для printWithClass
, ему нужна специальная сигнатура метода для связи с вызовом print
. Java не имеет множественной отправки, поэтому не может поставить неопределенную подпись в таблице методов и решить, что вызывать во время выполнения. Единственная верхняя граница на T
равна Object
, поэтому единственный метод, который соответствует, равен print(Object)
.
Ответ 4
Потому что дженерики java не являются дженериками, как вы думаете. Когда обобщенный код Java компилируется, вся информация о типе фактически удаляется, и остается только базовый известный тип. В этом случае этот тип Object
.
Обобщения в java - это действительно только обман компилятора, когда компилятор удаляет приведения, которые в противном случае были бы необходимы, и вызывая сдерживание времени компиляции. В конце концов, все, что осталось, это базовый тип, когда он фактически скомпилирован в байтовый код.
Этот процесс называется стиранием типа . Этот предыдущий вопрос полезен для понимания того, что на самом деле происходит.
Ответ 5
Потому что он может знать, что только во время выполнения, но на самом деле, поскольку java - это скомпилированный язык, а не сценарий, он определяется во время компиляции.
Дженерики java позволяют "тип или метод работать на объектах разных типов при обеспечении безопасности времени компиляции.
Вы можете, конечно, попробовать что-то вроде:
static <T extends String> void printWithClass(T t) {
print(t);
}
хотя это не то, что вам нужно, что невозможно, поскольку компилятор вызывает снимки.
Ответ 6
static <T> void printWithClass(T t) {
print(t);
}
будет объединен с
static void printWithClass(Object t) {
print(t);
}
Ответ 7
Обобщения интерпретируются компилятором, и они обеспечивают дополнительную проверку типов, чтобы избежать проблем с кастомным запуском. Информация об общем типе потеряна во время выполнения. Таким образом, во время выполнения, который получает printWithClass, это просто объект, а не String и, следовательно, ваш результат.
Ответ 8
Дополнительный пример для пояснения:
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