Pergunta

Ontem eu tive uma entrevista por telefone técnica de duas horas (o que eu passei, woohoo!), Mas eu completamente muffed-se a seguinte pergunta a respeito obrigatório em Java dinâmico. E é duplamente intrigante porque eu usei para ensinar este conceito para alunos de graduação, quando eu era TA há alguns anos, de modo a perspectiva de que eu dei-lhes desinformação é um pouco perturbador ...

Aqui está o problema me foi dada:

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

I afirmou que a saída deveria ter sido duas instruções de impressão separadas de dentro do método equals() substituído: em t1.equals(t3) e t3.equals(t3). O último caso é bastante óbvio, e com o primeiro caso, embora t1 tem uma referência do tipo Object, ele é instanciado como tipo de teste, de modo dinâmico de ligação deve chamar a forma substituído do método.

Aparentemente não. Meu entrevistador me incentivou a executar o programa mim, e eis que, havia apenas uma única saída do método substituído:. No t3.equals(t3) linha

A minha pergunta é, então, por quê? Como já mencionei, embora t1 é uma referência do tipo Object (de modo estático vinculativo invocar o método equals() de Object), a ligação dinâmica deve cuidar de invocar a versão mais específica do método baseado na instanciado tipo da referência. O que eu estou ausente?

Foi útil?

Solução

Java usa vinculativo para métodos sobrecarregados estática e dinâmica de ligação para os substituídos. No seu exemplo, o método equals está sobrecarregado (tem um tipo de param diferente do que Object.equals ()), de modo que o método chamado é vinculado ao referência tipo em tempo de compilação.

Alguns discussão href="http://forums.techarena.in/software-development/1070300.htm" aqui

O fato de que ele é o método equals não é realmente relevante, que não é um erro comum a sobrecarga em vez de substituí-lo, o que você já está ciente de com base em sua resposta para o problema na entrevista.

Edit: Uma boa descrição aqui também. Este exemplo está mostrando um problema semelhante relacionado com o tipo de parâmetro em vez disso, mas causada pelo mesmo problema.

Eu acredito que se a ligação foram realmente dinâmico, então qualquer caso em que o chamador eo parâmetro foram uma instância de teste resultaria no método substituído sendo chamado. Então t3.equals (O1) seria o único caso que não iria imprimir.

Outras dicas

O método equals de Test não substituir o método equals de java.lang.Object. Olhe para o tipo de parâmetro! A classe Test está sobrecarregando equals com um método que aceita um Test.

Se o método equals se destina a substituir, ele deve usar a anotação @ Override. Isso faria com que um erro de compilação ressaltar este erro comum.

Curiosamente, no código Groovy (que pode ser compilado para um arquivo de classe), todos, mas uma das chamadas iria executar a instrução de impressão. (A única comparando um teste para um objeto claramente não vai chamar a função Test.equals (Test).) Isto é porque Groovy faz digitação completamente dinâmico. Isto é particularmente interessante porque ele não tem quaisquer variáveis ??que são digitados explicitamente dinamicamente. Eu li em um par de lugares que se considerar que é prejudicial, como programadores esperam Groovy para fazer a coisa java.

Java não suporta co-variância nos parâmetros, apenas em tipos de retorno.

Em outras palavras, enquanto o seu tipo de retorno em um método de substituição pode ser um subtipo do que era na substituído, isso não é verdade para os parâmetros.

Se o seu parâmetro para iguais em objeto é objeto, colocando um igual com qualquer outra coisa em uma subclasse será uma sobrecarregado, não um método substituído. Assim, a única situação em que o método será chamado é quando o tipo estático do parâmetro é Test, como no caso do T3.

Boa sorte com o processo de entrevista de emprego! Eu adoraria ser entrevistado em uma empresa que pede a esses tipos de perguntas, em vez das habituais perguntas estruturas algo / dados que eu ensino meus alunos.

Eu acho que a chave reside no fato de que os método equals () não está em conformidade com a norma: Leva em outro objeto de teste, não objeto Object e, portanto, não é substituir o método equals (). Isso significa que você realmente só sobrecarregados-lo para fazer algo especial quando é dado objeto de teste, dando-lhe OBJETO chama Object.equals (Object o). Olhando para esse código através de qualquer IDE deve mostrar-lhe duas equals () métodos para teste.

O método está sobrecarregado em vez de anulado. Equals sempre tomar um objeto como parâmetro.

btw, você tem um item sobre isso em java eficaz de Bloch (que você deve possuir).

Alguns nota em ligação dinâmica (DD) e vinculação estática (SB) depois de pesquisar um pouco:

1.Timing executar : (Ref.1)

  • DB: em tempo de execução
  • SB: tempo compilador

2.Used para :

  • DB: substituir
  • SB: sobrecarga (estático, privado, final) (Ref.2)

Referência:

  1. Executar resolvedor média qual método preferem usar
  2. Porque não pode substituir método com estático modificador, privado ou final
  3. http: // javarevisited. blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

Se outro método é adicionado que substituições em vez de sobrecarregar ele vai explicar a chamada de ligação dinâmica em tempo de execução.

/ * Qual é a saída do seguinte programa? * /

public class DynamicBinding {
    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside @override: this is dynamic binding");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++);// prints 0
        t1.equals(t2);
        System.out.println(count++);// prints 1
        t1.equals(t3);
        System.out.println(count++);// prints 2
        t3.equals(o1);
        System.out.println(count++);// prints 3
        t3.equals(t3);
        System.out.println(count++);// prints 4
        t3.equals(t2);
    }
}

Eu encontrei um artigo interessante sobre a ligação estático vs. dinâmico. Ele vem com um pedaço de código para simulação dinâmica de ligação. Ele fez o meu código de uma forma mais legível.

https://sites.google.com/site/jeffhartkopf/covariance

Veja também este SO Pergunta, intimamente relacionados: substituindo o JAVA é igual a método peculiaridade

A resposta para a pergunta "por quê?" é que é como a linguagem Java é definido.

Para citar o artigo Wikipedia em Covariância e Contravariance :

Retorno tipo covariância é implementado na linguagem de programação Java versão J2SE 5.0. tipos de parâmetros têm para ser exatamente o mesmo (invariantes) para método de substituição, caso contrário, o método está sobrecarregado com um paralelo definição em seu lugar.

Outros idiomas são diferentes.

É muito claro, que não existe o conceito de substituir aqui. É a sobrecarga de método. o método Object() da classe Object leva parâmetro de referência do tipo de objeto e este método equal() leva parâmetro de referência do tipo de teste.

Vou tentar explicar isso através de dois exemplos que são as versões estendidas de alguns dos exemplos que me deparei online.

public class Test {

    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside of Test.equals ot type Object");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++); // prints 0
        o1.equals(t2);

        System.out.println("\n" + count++); // prints 1
        o1.equals(t3);

        System.out.println("\n" + count++);// prints 2
        t1.equals(t2);

        System.out.println("\n" + count++);// prints 3
        t1.equals(t3);

        System.out.println("\n" + count++);// prints 4
        t3.equals(o1);

        System.out.println("\n" + count++);// prints 5
        t3.equals(t3);

        System.out.println("\n" + count++);// prints 6
        t3.equals(t2);
    }
}

Aqui, para linhas com valores de contagem 0, 1, 2 e 3; temos referência de objeto o1 e t1 no método equals(). Assim, em tempo de compilação, o método equals() do Object.class arquivo será limitado.

No entanto, apesar de referência de t1 é objeto , tem intialization de classe de teste .
Object t1 = new Test();.
Portanto, em tempo de execução chama o public boolean equals(Object other) que é um

método substituído

. enter descrição da imagem aqui

Agora, para valores de contagem de 4 e 6, é novamente simples que t3 , que tem referência equals() e inicialização de teste está chamando método com o parâmetro como referências de objeto e é um

método sobrecarregado

OK!

Mais uma vez, para entender melhor o método o compilador irá chamar, apenas clique sobre o método e Eclipse irá destacar os métodos de semelhante tipos que ele acha que vai chamar no tempo de compilação. Se ele não recebe chamada em tempo de compilação, em seguida, esses métodos são um exemplo de método overridding.

enter descrição da imagem aqui

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