elvis - operador ternario multiple java
Diferencia entre C#y el operador ternario de Java(?:) (5)
Soy un novato de C # y me encuentro con un problema.
Hay una diferencia entre C # y Java cuando se trata con el operador ternario (
? :
.
En el siguiente segmento de código, ¿por qué no funciona la cuarta línea?
El compilador muestra un mensaje de error de que
there is no implicit conversion between ''int'' and ''string''
.
La quinta línea no funciona tan bien.
Ambas
List
son objetos, ¿no?
int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object
Sin embargo, el mismo código funciona en Java:
int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
: new ArrayList<String>()); //param: Object
¿Qué función de lenguaje falta en C #? Si hay alguno, ¿por qué no se agrega?
En cuanto a la parte genérica:
two > six ? new List<int>() : new List<string>()
En C #, el compilador intenta
convertir
las partes de expresión de la derecha a algún tipo común;
Como
List<int>
y
List<string>
son dos tipos construidos distintos, uno no se puede convertir al otro.
En Java, el compilador intenta encontrar un supertipo común en lugar de convertir, por lo que la compilación del código implica el uso implícito de wildcards y borrado de tipos ;
two > six ? new ArrayList<Integer>() : new ArrayList<String>()
tiene el tipo de compilación de
ArrayList<?>
(en realidad, también puede ser
ArrayList<? extends Serializable>
o
ArrayList<? extends Comparable<?>>
, dependiendo del contexto de uso, ya que ambos son supertipos genéricos comunes) y el tipo de tiempo de ejecución de raw
ArrayList
(ya que es el supertipo raw común).
Por ejemplo (pruébelo usted mismo) ,
void test( List<?> list ) {
System.out.println("foo");
}
void test( ArrayList<Integer> list ) { // note: can''t use List<Integer> here
// since both test() methods would clash after the erasure
System.out.println("bar");
}
void test() {
test( true ? new ArrayList<Object>() : new ArrayList<Object>() ); // foo
test( true ? new ArrayList<Integer>() : new ArrayList<Object>() ); // foo
test( true ? new ArrayList<Integer>() : new ArrayList<Integer>() ); // bar
} // compiler automagically binds the correct generic QED
Esto es bastante sencillo. No hay conversión implícita entre cadena e int. El operador ternario necesita que los dos últimos operandos tengan el mismo tipo.
Tratar:
Write(two > six ? two.ToString() : "6");
Las respuestas dadas son buenas; Les agregaría que esta regla de C # es una consecuencia de una guía de diseño más general. Cuando se le pide que infiera el tipo de una expresión a partir de una de varias opciones, C # elige la mejor de ellas . Es decir, si le da a C # algunas opciones como "Jirafa, Mamífero, Animal", entonces podría elegir el más general - Animal - o podría elegir el más específico - Jirafa - dependiendo de las circunstancias. Pero debe elegir una de las opciones que realmente se le dio . C # nunca dice "mis elecciones son entre Cat y Dog, por lo tanto deduciré que Animal es la mejor opción". Esa no era una opción dada, por lo que C # no puede elegirla.
En el caso del operador ternario, C # intenta elegir el tipo más general de int y string, pero tampoco lo es el tipo más general. En lugar de elegir un tipo que no fue una elección en primer lugar, como un objeto, C # decide que no se puede inferir ningún tipo.
También noto que esto está de acuerdo con otro principio de diseño de C #: si algo parece mal, dígaselo al desarrollador. El lenguaje no dice "Voy a adivinar lo que quisiste decir y seguir adelante si puedo". El lenguaje dice "Creo que has escrito algo confuso aquí, y voy a contarte sobre eso".
Además, observo que C # no razona desde la variable hasta el valor asignado , sino más bien en la otra dirección. C # no dice "estás asignando a una variable de objeto, por lo tanto, la expresión debe ser convertible en objeto, por lo tanto, me aseguraré de que sea". Más bien, C # dice "esta expresión debe tener un tipo, y debo poder deducir que el tipo es compatible con el objeto". Como la expresión no tiene un tipo, se produce un error.
Mirando a través de la sección de especificación de lenguaje C # 5 7.14: Operador condicional podemos ver lo siguiente:
Si x tiene tipo X e y tiene tipo Y entonces
Si existe una conversión implícita (§6.1) de X a Y, pero no de Y a X, entonces Y es el tipo de expresión condicional.
Si existe una conversión implícita (§6.1) de Y a X, pero no de X a Y, entonces X es el tipo de expresión condicional.
De lo contrario, no se puede determinar ningún tipo de expresión y se produce un error en tiempo de compilación
En otras palabras: intenta encontrar si x e y se pueden convertir entre
sí
y, de lo contrario, se produce un error de compilación.
En nuestro caso,
int
y
string
no tienen conversión explícita o implícita, por lo que no se compilará.
Compare esto con la sección 15.25 de la especificación del lenguaje Java 7: Operador condicional :
- Si el segundo y tercer operandos tienen el mismo tipo (que puede ser el tipo nulo), entonces ese es el tipo de la expresión condicional. ( NO )
- Si uno de los operandos segundo y tercero es de tipo primitivo T, y el tipo del otro es el resultado de aplicar la conversión de boxeo (§5.1.7) a T, entonces el tipo de expresión condicional es T. ( NO )
- Si uno de los operandos segundo y tercero es del tipo nulo y el tipo del otro es un tipo de referencia, entonces el tipo de la expresión condicional es ese tipo de referencia. ( NO )
- De lo contrario, si el segundo y tercer operandos tienen tipos que son convertibles (§5.1.8) a tipos numéricos, entonces hay varios casos: ( NO )
- De lo contrario, el segundo y tercer operandos son de los tipos S1 y S2 respectivamente. Deje que T1 sea el tipo que resulta de aplicar la conversión de boxeo a S1, y que T2 sea el tipo que resulta de aplicar la conversión de boxeo a S2.
El tipo de expresión condicional es el resultado de aplicar la conversión de captura (§5.1.10) a lub (T1, T2) (§15.12.2.7). ( SI )
Y, mirando la
sección 15.12.2.7.
Inferir argumentos de tipo Basado en argumentos reales
, podemos ver que trata de encontrar un antepasado común que sirva como el tipo utilizado para la llamada que lo aterriza con
Object
.
Object
es
un argumento aceptable para que la llamada funcione.
Tanto en Java como en C # (y en la mayoría de los otros lenguajes), el resultado de una expresión tiene un tipo.
En el caso del operador ternario, hay dos subexpresiones posibles evaluadas para el resultado y ambas deben tener el mismo tipo.
En el caso de Java, una variable
int
se puede convertir en un
Integer
mediante autoboxing.
Ahora, dado que tanto
Integer
como
String
heredan de
Object
, se pueden convertir al mismo tipo mediante una conversión de estrechamiento simple.
Por otro lado, en C #, un
int
es un primitivo y no hay conversión implícita a una
string
o cualquier otro
object
.