Sou um novato em C # e acabei de encontrar um problema. Há uma diferença entre C # e Java ao lidar com o operador ternário (? :
).
No segmento de código a seguir, por que a 4ª linha não funciona? O compilador mostra uma mensagem de erro de there is no implicit conversion between 'int' and 'string'
. A 5ª linha não funciona tão bem. Ambos List
são objetos, não são?
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
No entanto, o mesmo código funciona em 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
Qual recurso de linguagem está faltando no C #? Se houver, por que não foi adicionado?
java
c#
ternary-operator
conditional-operator
blackr1234
fonte
fonte
int
não é um objeto. É um primitivo.ArrayList<String>
eArrayList<Integer>
torna - se apenasArrayList
no bytecode. Isso significa que eles parecem ser exatamente do mesmo tipo em tempo de execução. Aparentemente, em C #, eles são de tipos diferentes.Respostas:
Olhando através da seção 7.14 de especificação da linguagem C # 5: Operador condicional , podemos ver o seguinte:
Em outras palavras: ele tenta descobrir se xey podem ou não ser convertidos um para o outro e se não, ocorre um erro de compilação. No nosso caso
int
estring
não tem nenhuma conversão explícita ou implícita por isso não irá compilar.Compare isso com a seção 15.25 da Especificação da linguagem Java 7: Operador condicional :
E, olhando a seção 15.12.2.7. Inferindo argumentos de tipo com base em argumentos reais , podemos ver que ele tenta encontrar um ancestral comum que servirá como o tipo usado para a chamada que o faz chegar
Object
.Object
é um argumento aceitável para que a chamada funcione.fonte
As respostas dadas são boas; Eu acrescentaria a eles que essa regra do C # é uma consequência de uma diretriz de design mais geral. Quando solicitado a inferir o tipo de uma expressão a partir de uma das várias opções, o C # escolhe a melhor delas . Ou seja, se você der ao C # algumas opções como "Girafa, Mamífero, Animal", ele pode escolher a mais geral - Animal - ou pode escolher a mais específica - Girafa - dependendo das circunstâncias. Mas ele deve escolher uma das escolhas que realmente foi dado . C # nunca diz "minhas escolhas são entre gato e cachorro, portanto, vou deduzir que Animal é a melhor escolha". Essa não foi uma escolha dada, então C # não pode escolhê-la.
No caso do operador ternário, C # tenta escolher o tipo mais geral de int e string, mas nenhum é o tipo mais geral. Em vez de escolher um tipo que não era uma escolha em primeiro lugar, como objeto, C # decide que nenhum tipo pode ser inferido.
Também observo que isso está de acordo com outro princípio de design do C #: se algo parecer errado, diga ao desenvolvedor. A linguagem não diz "Vou adivinhar o que você quis dizer e continuar confuso, se puder". A linguagem diz: "Acho que você escreveu algo confuso aqui e vou falar sobre isso".
Além disso, observo que C # não raciocina da variável para o valor atribuído , mas sim a outra direção. C # não diz "você está atribuindo a uma variável de objeto, portanto, a expressão deve ser conversível em objeto, portanto, certificarei que seja". Em vez disso, C # diz "esta expressão deve ter um tipo e devo ser capaz de deduzir que o tipo é compatível com o objeto". Como a expressão não possui um tipo, é produzido um erro.
fonte
int? b = (a != 0 ? a : (int?) null)
. Há também essa pergunta , junto com todas as perguntas vinculadas ao lado. Se você continuar seguindo os links, há alguns. Em comparação, nunca ouvi falar de alguém enfrentando um problema do mundo real com a maneira como o Java o faz.Em relação à parte dos genéricos:
No C #, o compilador tenta converter as partes da expressão à direita em algum tipo comum; desde
List<int>
eList<string>
são dois tipos construídos distintos, um não pode ser convertido no outro.Em Java, o compilador tenta encontrar um supertipo comum em vez de converter, portanto, a compilação do código envolve o uso implícito de curingas e eliminação de tipo ;
tem o tipo de compilação de
ArrayList<?>
(na verdade, pode ser tambémArrayList<? extends Serializable>
ouArrayList<? extends Comparable<?>>
, dependendo do contexto de uso, uma vez que ambos são supertipos genéricos comuns) e o tipo de tempo de execução brutoArrayList
(já que é o supertipo bruto comum).Por exemplo (teste você mesmo) ,
fonte
Write(two > six ? new List<object>() : new List<string>());
também não funciona.ArrayList<String>
eArrayList<Integer>
seriamArrayList
(o tipo bruto), mas o tipo inferido para este operador ternário éArrayList<?>
(o tipo curinga). De modo mais geral, o fato de o Java runtime implementar genéricos por meio de eliminação de tipo não tem efeito no tipo de tempo de compilação de uma expressão.two > six ? new ArrayList<Integer>() : new ArrayList<String>()
é oArrayList<? extends Serializable&Comparable<?>>
que significa que você pode atribuí-lo a uma variável de tipoArrayList<? extends Serializable>
e também a uma variável de tipoArrayList<? extends Comparable<?>>
, mas é claroArrayList<?>
, que é equivalente aArrayList<? extends Object>
, também funciona.Em Java e C # (e na maioria das outras linguagens), o resultado de uma expressão tem um tipo. No caso do operador ternário, existem duas subexpressões possíveis avaliadas para o resultado e ambas devem ser do mesmo tipo. No caso do Java, uma
int
variável pode ser convertida emInteger
autoboxing. Agora, uma vez que ambosInteger
eString
herdam deObject
, eles podem ser convertidos para o mesmo tipo por uma conversão de estreitamento simples.Por outro lado, em C #, an
int
é um primitivo e não há conversão implícita parastring
ou qualquer outroobject
.fonte
int
paraobject
.Integer/String
paraObject
não é uma conversão de estreitamento, é exatamente o oposto :-)Integer
eString
também implementarSerializable
e,Comparable
portanto, atribuições a qualquer um deles funcionarão bem, por exemplo,Comparable<?> c=condition? 6: "6";
ouList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
são códigos Java legais.Isso é muito simples. Não há conversão implícita entre string e int. o operador ternário precisa que os dois últimos operandos tenham o mesmo tipo.
Experimentar:
fonte