java compilador oddity: campo declarado na mesma classe, mas “não visível”
-
05-07-2019 - |
Pergunta
O compilador eclipse se recusa a compilar o código a seguir, afirmando que o campo é não é visível. (Compilador J Aspecto da IBM também se recusa, afirmando que "s não pôde ser resolvido") Por que isso?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
A especificação de linguagem Java afirma:
Caso contrário, dizemos que há padrão acesso, que somente quando é permitido o acesso ocorre de dentro do pacote no qual o tipo é declarado.
A forma como eu entendo, o campo é declarado e acessados ??na mesma unidade de compilação, portanto, dentro do mesmo pacote, e deve, portanto, ser acessível.
Ainda mais estranhamente, adicionando uma entrada de ar de ? extends Test
para Test
faz, isto é, os compila o seguinte código no campo visíveis:
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
Tem me deparei com um erro do compilador, ou mal compreendido o Java Spec?
Editar: Estou em outro computador agora. Aqui, javac aceita o código, mas eclipse ainda não. Versões desta máquina:
Eclipse Platform
Versão: 3.4.2 Construir ID: M20090211-1700
JDK 1.6.0
Editar 2 Na verdade, javac aceita o código. Eu tinha testado executando a compilação de formiga, que usa da IBM Ascpect J compilador ...
Solução
Tente isto:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[Edit para esclarecer]:
getClass().cast(o)
retorna um objeto do tipo 'capture#1-of? extends Test
' e não Test
. Então, o problema está relacionado aos genéricos e como os compilador trata de TI. Eu não sei os detalhes da especificação sobre os genéricos, mas dado que alguns compiladores (por comentários aqui) não aceitar o seu código, então este é ou uma lacuna nas especificações ou alguns destes compiladores não estão inteiramente de acordo com as especificações.
[últimos pensamentos]:
Eu acredito que o compilador eclipse é realmente (com cuidado) correto aqui. O objecto o
pode, de facto, ser uma extensão de Teste (e definido em outro pacote) e o compilador não tem maneira de saber se isso for de facto o caso, ou não. Por isso, é tratá-la como o pior caso de uma instância de uma extensão definida em outro pacote. Teria sido super correto se adicionar um qualificador final
para Test
classe teria permitido o acesso a s
campo, mas isso não acontece.
Outras dicas
Bem, vamos ver. Eu diria que o compilador não pode garantir adequadamente que foo()
será chamado por alguma entidade dentro do pacote e, portanto, não pode garantir que s
é visível. Por exemplo, adicione
protected void bar() {
foo();
}
e, em seguida, em algum Banana
subclasse em outro pacote
public void quux() { bar(); }
e oops! rendimentos getClass()
Banana
, que não pode ver s
.
Edit: Em certo sentido, other.package.Banana não Have a s
campo. Se Banana foram no mesmo pacote, poderia ainda ter sua própria propriedade s
, e teria que se referem a Test
de s
via super
.
Eu não posso reproduzir o que você está dizendo. Estes dois fina compilação para mim sem aviso, erro ou qualquer coisa com javac diretamente.
WinXP, javac 1.6.0_16
Não, eu tentei com eclipse (v3.4.1, Construir ID: M20080911-1700) e para o primeiro que diz:
The field Test.s is not visible
Pelo menos para o nível Compiler Compliance 1.6 e 1.5.
A coisa ser engraçado, se você olhar para as opções de Quick-Fix it enumera uma resolução Change to 's'
. O que, naturalmente, não resolve o problema. Portanto, o compilador eclipse eo "gerador" quick-fix parecem ter opiniões diferentes sobre isso também; -)
Para o nível Compiler Compliance 1.4 (como era de se esperar) em eclipse para o primeiro que eu get
s cannot be resolved or is not a field
e para o segundo I get
Type mismatch: cannot convert from Object to Test
Se eu especificar -source 1.4
e target -1.4
na linha de comando diretamente javac
diz para o primeiro
cannot find symbol
e para o segundo I get
incompatible types
Na verdade, em quase todos os casos, exceto quando exigido pela Generics, é melhor (e mais seguro) para usar Java operador de conversão. Eu discuti-lo aqui . Java operador de conversão faz olhar sobre detalhado, mas é a ferramenta certa aqui.
Substituir método cast
com as compilações operador muito bem em Eclipse.
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
Eu acho que alphazero está correto aqui , que Eclipse é um pouco mais cauteloso.
Muito estranho. Por uma razão desconhecida (para mim), o compilador eclipse requer uma conversão explícita:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
Enquanto o código perfeitamente compilado sem o elenco com o JDK da Sun (estou executando a versão 1.6.0_16 no GNU / Linux).