¿Por qué List[scala.Int]
tipo de borrado al List[Object]
mismo tiempo Integer
en List[java.lang.Integer]
que parece ser preservado? Por ejemplo, javap
para
object Foo {
def fooInt: List[scala.Int] = ???
def fooInteger: List[java.lang.Integer] = ???
}
salidas
public scala.collection.immutable.List<java.lang.Object> fooInt();
public scala.collection.immutable.List<java.lang.Integer> fooInteger();
donde vemos Integer
que se conserva en el segundo caso. La documentación del estado
Reemplazar todos los parámetros de tipo de tipos genéricos con sus límites o
Object
si los parámetros de tipo son ilimitados.
Es esto quizás debido a la cláusula de "límites"? Si es así, en la que se especifica esta cota?
Estoy no un desarrollador Scala, tomar esto con un grano de sal. El borrado es el mismo:
public static scala.collection.immutable.List<java.lang.Object> fooInt();
descriptor: ()Lscala/collection/immutable/List;
public static scala.collection.immutable.List<java.lang.Integer> fooInt();
descriptor: ()Lscala/collection/immutable/List;
mirar el descriptor
parámetro; esto es lo que se hace referencia en los sitios de llamadas a nivel de código de bytes.
Cuando sólo tiene que hacer javap
, se "tramposos" un poco mirando el Signature
parámetro (leer más) para que se le muestra esta pequeña mentira inofensiva.
Ahora pensar en ello. Vamos a tomar este método y lo colocan en la clase A
:
static List<Integer> test() {
return null; // or whatever that is not the point
}
compilamos ella, comparten el .class
archivo a otra persona. Que alguien más lo usa en esta forma: (sin llegar a tener el código fuente para A
).
public void testMe() {
Integer x = A.test().get(0);
}
Si nos fijamos en el byte de código, verá:
5: invokeinterface #3, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
10: checkcast #4 // class java/lang/Integer
Hay una pregunta inmediata que tiene que surgir: ¿cómo saber acerca de Integer
(a través de que checkcast
) si se borran los genéricos? La respuesta es la opción Signature
que se genera cuando A
se compila, o en sus casos:
()Lscala/collection/immutable/List<Ljava/lang/Object;>; //fooInt
()Lscala/collection/immutable/List<Ljava/lang/Integer;>; // fooInteger
Esta Signature
información es la que se utiliza por el compilador para hacer cumplir la seguridad de tipos en callsites , a través de los controles de tiempo de ejecución; Si este campo no estaría presente - que habría sido imposible.
Ahora a por qué el Signature
de la scalac
genera Object
(por lo tanto la seguridad de tipos cero para las personas que llaman) es algo que las direcciones duplicadas. He tratado de leer el tema y no es una lectura fácil que voy a ir con "Confío en ti".
Un poco más explicaciones: Signature
aparecido en java-5
cuando los genéricos, donde añaden. Hasta entonces, todas las llamadas sitios donde hace referencia mediante descriptor
, cambiantes que a Signature
cambio significaría que el código existente se rompería; por lo tanto nunca hecho. De este modo Signature
se convirtió en opcional y se utiliza de una manera diferente - para checkcast
. Al menos esto es lo que estoy fuertemente inclinado a asumir :)