¿Por qué Java no hace inferencia de tipos?

44

Siempre me he preguntado por qué Java no hace inferencia de tipos dado que el lenguaje es lo que es y su VM es muy madura. Google's Go es un ejemplo de un idioma con excelente inferencia de tipos y reduce la cantidad de tipeo que uno tiene que hacer. ¿Hay alguna razón especial detrás de que esta característica no sea parte de Java?

cobie
fuente
3
reglas de compatibilidad con versiones anteriores en un lenguaje tan antiguo y popular como java
ratchet freak
99
@ratchetfreak ¿Podría la inferencia de tipos no agregarse de forma compatible con versiones anteriores? Los programas más antiguos solo proporcionarían más información de tipo de la necesaria.
1
Puede tener algunos efectos no deseados cuando se usa con Type Erasure. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates
44
Tenga en cuenta que Java 8 traerá mucha inferencia de tipos con su función Lambda: puede escribir lambdas complejas sin mencionar ningún tipo y todo lo que se infiere.
Joachim Sauer
44
La inferencia de tipo variable local está llegando a Java: JEP 286: Inferencia de tipo variable local
Jimmy Page

Respuestas:

60

Técnicamente hablando, Java tiene inferencia de tipos cuando usa genéricos. Con un método genérico como

public <T> T foo(T t) {
  return t;
}

El compilador analizará y comprenderá que cuando escribes

// String
foo("bar");
// Integer
foo(new Integer(42));

Se devolverá una cadena para la primera llamada y un número entero para la segunda llamada en función de lo que se ingresó como argumento. Obtendrá la verificación de tiempo de compilación adecuada como resultado. Además, en Java 7, uno puede obtener algunas inferencias de tipo adicionales al instanciar genéricos como este

Map<String, String> foo = new HashMap<>();

Java es lo suficientemente amable como para completar los corchetes angulares en blanco para nosotros. Ahora, ¿por qué Java no admite la inferencia de tipos como parte de la asignación de variables? En un momento, hubo una RFE para la inferencia de tipos en declaraciones variables, pero esto se cerró como "No se solucionará" porque

Los humanos se benefician de la redundancia de la declaración de tipo de dos maneras. Primero, el tipo redundante sirve como documentación valiosa: los lectores no tienen que buscar la declaración de getMap () para averiguar qué tipo devuelve. En segundo lugar, la redundancia permite al programador declarar el tipo deseado y, por lo tanto, beneficiarse de una verificación cruzada realizada por el compilador.

El colaborador que cerró esto también notó que simplemente se siente "poco parecido a Java", con lo que estoy de acuerdo. La verbosidad de Java puede ser tanto una bendición como una maldición, pero hace que el lenguaje sea lo que es.

Por supuesto, ese RFE en particular no fue el final de esa conversación. Durante Java 7, esta característica se consideró nuevamente , y se crearon algunas implementaciones de prueba, incluida una por el propio James Gosling. Una vez más, esta característica finalmente fue derribada.

Con el lanzamiento de Java 8, ahora obtenemos inferencia de tipos como parte de lambdas como tal:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

El compilador de Java puede mirar el método Collections#sort(List<T>, Comparator<? super T>)y luego la interfaz Comparator#compare(T o1, T o2)y determinarlo, firsty seconddebería ser Stringasí permitiendo que el programador renuncie a tener que replantear el tipo en la expresión lambda.

Angustia
fuente
55
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- Sí, si es así, HashMap<String, Integer> map = foo.getMap()estoy de acuerdo - en C # normalmente no uso varen tales casos, aunque podría. Pero este argumento no retiene el agua si es así HashMap<String, Integer> map = new HashMap<String, Integer>(). Esto es una verdadera redundancia y no veo ningún beneficio en tener que escribir el nombre del tipo dos veces. Y cómo me beneficiaría aquí de una verificación cruzada del compilador, no lo entiendo en absoluto.
Konrad Morawski
99
Sí, y eso es bueno para los genéricos, pero aún extraño el equivalente de C # varen Java.
Konrad Morawski
15
En cuanto a que es "poco parecido a Java", esta historia (cursi) viene a mi mente;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski
66
Además, el tipo de retorno de una función a menudo es obvio o innecesario, e incluso en los casos en que no es su IDE puede marcar el tipo en medio segundo.
Phoshi
8
Creo que la cita oficial Humans benefit from the redundancyes la verdadera respuesta a la pregunta actual, porque cada justificación que he leído parece una excusa gratuita, infundada y ridícula de los diseñadores de Java: tanto C # como C ++ tienen la característica, los diseñadores de C # y C ++ no son menos competentes que Java, y todos están felices de usarlo. ¿Qué es lo que hace que los desarrolladores de Java sean diferentes de los desarrolladores de C # o C ++? Es por eso que estoy de acuerdo con @KonradMorawski, "Así es como se hacen las cosas aquí" parece ser nuevamente la verdadera razón detrás de las escenas para eso.
paercebal
16

Bueno, en primer lugar, la inferencia de tipos no tiene nada que ver con la madurez del tiempo de ejecución, ya sea que el tiempo de ejecución sea una CPU de 30 años o una VM que sea tan nueva que los bits sigan siendo brillantes. Se trata del compilador.

Dicho esto, está permitido para los genéricos, la razón por la cual no está permitido para los tipos no genéricos parece ser debido a la filosofía: no hay nada que impida que los diseñadores lo agreguen.

Actualización: parece que Java 10 lo admite: http://openjdk.java.net/jeps/286

jmoreno
fuente
5

Hasta donde yo sé, cuando Java se diseñó a principios de los años noventa, la inferencia de tipos no era tan popular entre los lenguajes convencionales (pero ya era un concepto muy conocido, por ejemplo, en ML). Entonces, puedo imaginar que la inferencia de tipos probablemente no fue compatible porque Java estaba dirigido a programadores provenientes de C ++, Pascal u otros lenguajes convencionales que no lo tenían (principio de menor sorpresa).

Además, uno de los principios de diseño de Java es escribir cosas explícitamente para asegurarse de que el programador y el compilador tengan la misma comprensión del código: la duplicación de información reduce las posibilidades de errores. Por supuesto, puede ser una cuestión de gusto si escribir algunos caracteres más vale la seguridad adicional que proporciona, pero esta fue la filosofía de diseño seguida para Java: escribir cosas explícitamente.

No sé si Java obtendrá inferencia de tipos en el futuro, pero en mi opinión, eso sería una gran revolución para el lenguaje (como Glenn Nelson mencionó, se describió como "no parecido a Java") y luego uno podría considerar dejar caer el nombrar Java a favor de un nuevo nombre.

Si desea usar un lenguaje JVM con inferencia de tipos, puede usar Scala.

Giorgio
fuente
2

Se me ocurren algunas posibles razones. Una es que la escritura explícita es autodocumentada. Java generalmente hace de esto una prioridad sobre la concisión. Otra razón podría ser en casos donde el tipo es algo ambiguo. Como cuando un tipo o cualquier subtipo puede satisfacer una rutina. Digamos que quiere usar una Lista, pero alguien aparece y usa un método exclusivo de ArrayList. El JIT inferiría una ArrayList y continuaría incluso si quisiera un error de compilación.

jiggy
fuente
8
¿Cómo funciona GridBagLayout gridbag = new GridBagLayout (); agregar a la auto documentación? Es pura repetición.
Anders Lindén
3
Se desambigua de un caso donde los dos tipos no son iguales. Podría asignar fácilmente una instancia de una subclase.
jiggy
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- No entiendo esto un poco. La inferencia de tipos tiene lugar en el momento de instaurar una variable, no de invocar un método en ella. ¿Podrías mostrar un ejemplo de lo que querías decir?
Konrad Morawski
2
@KonradMorawski: Supongo que quiere decir que si un método devuelve ArrayList, el tipo se deducirá de eso. Si desea tratar un tipo como algo diferente al tipo de retorno, no puede usar la inferencia. Sin embargo, no entiendo cómo esto podría ser un problema en una base de código en cualquier lugar cercano a cuerdo.
Phoshi
el JIT no jugaría ningún papel en la inferencia de tipos. La inferencia de tipos es un fenómeno en tiempo de compilación. y si se declara una variable como una referencia a una lista, tratando a los miembros acceso ArrayList en él no se escriba cheque y se obtiene un error de compilación
Sara
0

Contradice la recomendación bien establecida de declarar variables utilizando la interfaz más genérica que se ajuste a sus necesidades e inicializarlas con una clase de implementación adecuada, como en

Collection<String> names = new ArrayList<>();

Eficazmente,

var names = new ArrayList<String>();

no es más que azúcar sintáctica para

ArrayList<String> names = new ArrayList<String>();

Si lo desea, su IDE puede producirlo a partir de la new ArrayList<String>()expresión con "un clic" (refactorizar / crear variable local), pero recuerde que contradice la recomendación de "usar interfaces".

Ralf Kleberhoff
fuente