Pergunta

Quando a passagem de um objecto final (uma cadeia no código abaixo) mostra-se como nulo quando impressa a partir da classe interna anónimo. No entanto, quando um tipo de valor final ou final String linear é passado, o seu valor é apresentado correctamente. O que final realmente significa dentro do contexto da classe interna anônima e nulos porque é que o objeto passado?

public class WeirdInners
{
    public class InnerThing
    {
        public InnerThing()
        {
            print();
        }

        public void print(){

        }
    }

    public WeirdInners()
    {
        final String aString = "argh!".toString();
        final String bString = "argh!";
        System.out.println(aString);
        System.out.println(bString);


        InnerThing inner =new InnerThing(){
            public void print()
            {
                System.out.println("inner"+aString); // When called from constructor, this value is null.
                System.out.println("inner"+bString); // This value is correctly printed.
            }
        };

        inner.print();
    }


    public static void main(String[] args)
    {
        WeirdInners test1 = new WeirdInners();
    }

}

Este é um comportamento muito estranho para mim, porque a expectativa é que a string é um objeto, por que chamar de mudança toString() coisas?

Outras informações: Este comportamento só é observado usando Java 1.4, não em Java 5. Qualquer sugestão sobre uma solução alternativa? Não chamar toString() em uma cadeia existente é suficiente justo, mas uma vez que este é apenas um exemplo, tem implicações reais se eu executá-lo em um objeto não-String.

Foi útil?

Solução

Se você marcar a seção sobre compile-time constants nas JLS, você verá que chamar .toString() faz diferença. Como faz disparates como prefixo com false?null+"":.

O que é importante aqui é a ordem relativa da configuração dos campos que estão fechados ao longo e o construtor. Se você usar -target 1.4 ou posterior (o que não é o padrão no 1.4!), Em seguida, os campos serão copiados antes de chamar o super. Com a especificação antes de 1.3 este era bytecode ilegal.

Como é frequentemente o caso, nestes casos, javap -c é útil para ver o que o compilador javac está fazendo. A especificação é útil para entender por que (você deve ter paciência suficiente).

Outras dicas

Meu palpite seria que você desencadear um comportamento indefinido quando o construtor de InnerThing () passa (implicitamente) a sua this com o método de impressão de uma subclasse anônima de InnerThing enquanto o objeto não está totalmente construído. Este this por sua vez se baseia em uma referência implícita ao this de WierdInners.

Chamando movimentos .toString() inicialização do aString de tempo de compilação para a execução. Por que os difere comportamento indefinido entre Java 1.4 e 1.5 é uma implementação detalhe JVM provavelmente.

É perigoso para invocar métodos substituídos a partir de um construtor da superclasse, como eles vão ser chamado antes da subclasse-parte do objeto é inicializado.

Além disso, uma classe interna acessando variáveis ??final de um escopo encerrando vai realmente acesso cópias dessas variáveis ??(por isso eles precisam ser final), e estes campos copiados residem na subclasse anônima.

Eu suspeito que a bString razão é tratada de forma diferente é que seu valor é conhecido em tempo de compilação, o que permite que o compilador para inline o acesso de campo na subclasse, tornando o tempo em que campo é inicializado irrelevante.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top