Argumentos finais nos métodos de interface - qual é o objetivo?

189

Em Java, é perfeitamente legal definir finalargumentos nos métodos de interface e não obedecer isso na classe de implementação, por exemplo:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

No exemplo acima, bare bazpossui as finaldefinições opostas na classe VS a interface.

Da mesma maneira, nenhuma finalrestrição é imposta quando um método de classe estende outro, abstractou não.

Embora finaltenha algum valor prático dentro do corpo do método de classe, há algum ponto em especificar finalparâmetros de método de interface?

mindas
fonte
finalde qualquer maneira, não faz nada com tipos nativos, pois eles são copiados.
Paul Tomblin 21/03
11
Apenas como um ponto de discussão: eu apenas tentei e se duas interfacedefinições variam apenas no finalatributo de um argumento, os .classarquivos resultantes são byte por byte de forma idêntica (e, é claro, javap -vproduzem a mesma saída). O mesmo vale para duas classes que diferem apenas em finalum atributo, a propósito!
Joachim Sauer
2
@ Paul: faz exatamente a mesma coisa que com os tipos de referência: impede que os argumentos sejam modificados (se usados ​​na implementação).
Joachim Sauer
Tem tanta relevância quanto o público na assinatura do método.
21711 Robin
2
@Deepak: Vejo você pedindo exemplos de trabalho em todos os tipos de perguntas, mesmo quando isso não faz muito sentido. Eu acho que você deveria tentar aprender um pensamento abstrato: tente pensar em um problema sem ter algum código executável à sua frente. Isso o ajudará muito a longo prazo.
Joachim Sauer

Respostas:

103

Parece que não faz sentido. De acordo com a Java Language Specification 4.12.4 :

Declarar uma variável final pode servir como documentação útil de que seu valor não será alterado e pode ajudar a evitar erros de programação.

No entanto, um finalmodificador em um parâmetro de método não é mencionado nas regras para correspondência de assinaturas de métodos substituídos e não tem efeito no chamador, apenas no corpo de uma implementação. Além disso, conforme observado por Robin em um comentário, o finalmodificador em um parâmetro de método não afeta o código de bytes gerado. (Isso não é verdade para outros usos de final.)

Ted Hopp
fonte
O parâmetro method também se qualifica como variável? Obviamente, está na prática, mas está no contexto de especificação?
mindas
11
Ele não pode ser usado para corresponder assinaturas, pois não aparece no arquivo .class real. É apenas para o compilador.
Robin
@mindas - o JLS diz que existem sete tipos de variáveis . Os parâmetros de método são o quarto da lista.
Ted Hopp
3
A documentação é quase inútil, no entanto, uma vez que o finalmodificador não é imposto à classe de implementação. Sua assinatura de interface pode simplesmente mentir.
Stefan Haberl
A especificação da linguagem Java 8 agora diz que existem oito tipos de variáveis (acima das sete - eles adicionaram parâmetros lambda ). Os parâmetros do método ainda são o quarto da lista (pelo menos algumas coisas na vida parecem estáveis. :-)).
Ted Hopp
25

Alguns IDEs copiarão a assinatura do método abstract / interface ao inserir um método de implementação em uma subclasse.

Não acredito que faça alguma diferença para o compilador.

Edição: Embora eu acredite que isso era verdade no passado, eu não acho que os IDEs atuais façam isso mais.

Peter Lawrey
fonte
2
Ponto válido, embora eu não acho que houve muitos IDEs quando este recurso foi implementado (ou deixadas acidentalmente) :-)
mindas
2
Eu acho que está na categoria de static transientcampos. ;)
Peter Lawrey
1
E construtores públicos em uma classe abstrata.
precisa saber é o seguinte
1
IntelliJ faz isso. Minha versão é IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Dormouse
1
@Dormouse, é interessante, porque o Android Studio (3.1.4 Build # AI-173.4907809) não :(
The Godfather
18

As anotações finais dos parâmetros do método sempre são relevantes apenas para a implementação do método, nunca para o responsável pela chamada. Portanto, não há motivo real para usá-los nas assinaturas de métodos de interface. A menos que você queira seguir o mesmo padrão de codificação consistente, que requer parâmetros finais do método, em todas as assinaturas do método. Então é bom poder fazer isso.

jmg
fonte
6

Atualização: A resposta original abaixo foi escrita sem o entendimento completo da pergunta e, portanto, não a aborda diretamente. :)No entanto, deve ser informativa para quem deseja entender o uso geral da finalpalavra-chave.

Quanto à questão, gostaria de citar meu próprio comentário abaixo.

Acredito que você não seja forçado a implementar a finalidade de um argumento para deixá-lo livre para decidir se deve ser final ou não em sua própria implementação.

Mas sim, parece bastante estranho que você possa declará-lo finalna interface, mas não final na implementação. Teria feito mais sentido se:

uma. finala palavra-chave não era permitida para argumentos do método de interface (abstrato) (mas você pode usá-la na implementação) ou
b. declarar um argumento como finalna interface o forçaria a ser declarado finalna implementação (mas não forçado para não finais).


Posso pensar em duas razões pelas quais uma assinatura de método pode ter finalparâmetros: Beans e Objetos (na verdade, ambos são a mesma razão, mas contextos ligeiramente diferentes ) .

Objetos:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

A finalpalavra-chave garantiu que não criaríamos acidentalmente uma nova panela local , mostrando um erro de compilação quando tentamos fazê-lo. Isso garantiu que o caldo de galinha fosse adicionado à panela original que o addChickenmétodo obteve. Compare isso com o local addVegetablesonde perdemos a couve-flor, porque ela foi adicionada a uma nova panela de cozinha local, em vez da panela original.

Feijão: é o mesmo conceito que objetos (como mostrado acima) . Beans são essencialmente Objects em Java. No entanto, os beans (JavaBeans) são usados ​​em vários aplicativos como uma maneira conveniente de armazenar e transmitir uma coleção definida de dados relacionados. Assim como addVegetablespoderia atrapalhar o processo de criação, criando uma nova panela StringBuildere jogando-a fora com a couve-flor, também poderia fazer o mesmo com uma panela JavaBean .

ADTC
fonte
Está bem. Não é realmente uma resposta correta para a pergunta (já que não sou obrigado a fazer com que minha implementação do método da interface assuma argumentos finais, mesmo que a interface o diga), mas ainda é útil, pois é uma boa explicação de por que tornar os parâmetros do método final é uma boa ideia para começar.
Phil
Acredito que você não seja forçado a implementar a finalidade de um argumento para deixá-lo livre para decidir se deve ser final ou não em sua própria implementação. Mas sim, parece bastante estranho que você possa declarar finalna interface, mas não final na implementação. Teria feito mais sentido se (a) a final palavra-chave não fosse permitida para argumentos do método de interface (abstrato) (mas você pode usá-la na implementação) ou (b) declarar um argumento como finalna interface forçaria a sua declaração finalna implementação (mas não forçado a não finais).
ADTC
"Não é realmente uma resposta correta para a pergunta" Você está certo, eu não sei o que estava pensando ... deve ter sido as primeiras horas da madrugada de julho ou algo assim. :)
ADTC
2

Acredito que possa ser um detalhe supérfluo, pois se é final ou não, é um detalhe de implementação.

(Mais ou menos como declarar métodos / membros em uma interface como pública.)

Peter
fonte