2012-02-16 17 views
11

considerar lo siguiente:genéricos de Java - Tipo deducir

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

Imprime objeto: ABC. ¿Por qué no imprime String: abc?

+0

ver esta pregunta en conseguir el tipo de T - [Obtener Genérico tipo de clase en tiempo de ejecución] (http: // stackoverflow .com/questions/3403909/get-generic-type-of-class-at-runtime) – csturtz

Respuesta

4

Java admite la anulación de método (enlace de tipo dinámico), pero no lo que está intentando lograr (la sobrecarga es un polimorfismo estático y no dinámico).

Para lograr lo que desea lograr en Java, necesita un doble despacho.

Visitor Pattern debe ser tu amigo aquí.

Le he escrito un ejemplo de código.

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

Porque los genéricos de java no son genéricos de la manera en que usted cree que son. Cuando se compila el código java genérico, toda la información de tipo se elimina realmente, y solo queda el tipo conocido de base. En este caso, ese tipo es Object.

Los genéricos en java son en realidad engaños del compilador, donde el compilador elimina los moldes que de otro modo serían necesarios e induce una restricción de tiempo de compilación. Al final, todo lo que queda es el tipo base cuando esto realmente se compila en código de bytes.

Este proceso se llama tipo borrado. Este previous question es útil para comprender lo que realmente sucede.

0

Porque puede saberlo solo en tiempo de ejecución, pero en realidad, dado que Java es un lenguaje compilado y no uno, se está decidiendo en tiempo de compilación.

Los genéricos de java permiten "un tipo o método para operar en objetos de varios tipos al proporcionar en tiempo de compilación tipo de seguridad".

Por supuesto, puede intentar algo como:

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

aunque no es lo que está buscando, lo cual no es posible ya que el compilador tiene la última palabra.

10

Esto es debido a Java type erasure: su

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

es en realidad un azúcar sintáctica en la parte superior de

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

Para ser justos, que "azúcar sintáctico" permite al compilador hacer algo muy bonito y comprobación importante, pero en tiempo de ejecución solo hay una copia del método printWithClass, y utiliza java.lang.Object como tipo de variable t.

Si ha experimentado genéricos en otros idiomas (C#, C++ templates, Ada), el borrado de tipo contrastaría con lo que usted sabe, pero así es como funciona en la portada.

+0

No se trata de borrado de tipo, sino de los límites genéricos disponibles _en tiempo de compilación_. Si usamos '', el resultado es 'String: abc'. –

+1

@Daniel absolutamente - porque el método se convertiría en 'static void printWithClass (String t)' a continuación. Pero seguiría siendo un método único, con un único tipo, enlazado en tiempo de compilación para llamar a una única sobrecarga del método estático de "impresión". – dasblinkenlight

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

se comipled a

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

genéricos son interpretados por el compilador y hacen cumplir verificación de tipos adicional para evitar cualquier problema de tiempo de ejecución de fundición. La información del tipo genérico es lost at runtime. Entonces, en tiempo de ejecución, lo que printWithClass recibe es solo objeto y no String y, por lo tanto, su resultado.

4

No se trata de borrado de tipo, es un problema de compilación y lo mismo sucedería si la JVM almacenó los métodos genéricos en tiempo de ejecución. Tampoco se trata de la inferencia de tipo: el compilador infiere <String> como era de esperar.

El problema es que cuando el compilador genera código para printWithClass, necesita una firma de método específica para asociar con la llamada print. Java no tiene despacho múltiple, por lo que no puede poner una firma vaga en la tabla de métodos y decidir qué invocar en tiempo de ejecución. El único límite superior en T es Object, por lo que el único método que coincide es print(Object).

0

ejemplo adicional para aclarar:

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

Esta impresora:

Number: 1234