Qual é o objetivo da "classe final" em Java?


569

Estou lendo um livro sobre Java e ele diz que você pode declarar toda a classe como final . Não consigo pensar em nada onde eu usaria isso.

Eu sou apenas novo em programação e gostaria de saber se os programadores realmente usam isso em seus programas . Se o fizerem, quando o usarão para que eu possa entender melhor e saber quando usá-lo.

Se o Java é orientado a objetos e você declara uma classe final, isso não impede a idéia de que a classe tenha as características dos objetos?

Respostas:


531

Antes de tudo, recomendo este artigo: Java: Quando criar uma classe final


Se o fizerem, quando o usarão para que eu possa entender melhor e saber quando usá-lo.

Uma finalclasse é simplesmente uma classe que não pode ser estendida .

(Isso não significa que todas as referências a objetos da classe agiriam como se fossem declaradas como final .)

Quando é útil declarar uma classe como final, é abordado nas respostas desta pergunta:

Se o Java é orientado a objetos e você declara uma classe final, isso não impede a idéia de que a classe tenha as características dos objetos?

Em certo sentido, sim.

Ao marcar uma classe como final, você desativa um recurso poderoso e flexível do idioma para essa parte do código. Algumas classes, no entanto, não devem (e em certos casos não podem ) ser projetadas para levar em consideração a subclasse de uma maneira boa. Nesses casos, faz sentido marcar a turma como final, mesmo que isso limite o POO. (Lembre-se, no entanto, que uma classe final ainda pode estender outra classe não-final.)


39
Para adicionar à resposta, um dos princípios do Java eficaz é favorecer a composição sobre a herança. O uso da palavra-chave final também ajuda a aplicar esse princípio.
Riggy

9
"Você faz isso principalmente por razões de eficiência e segurança". Eu ouço essa observação com frequência (até a Wikipedia afirma isso), mas ainda não entendo o raciocínio por trás desse argumento. Alguém se importa em explicar como, digamos, um java.lang.String não final teria terminado ineficiente ou inseguro?
MRA

27
@RA Se eu criar um método que aceite uma String como parâmetro, presumo que seja imutável, porque Strings são. Como resultado disso, sei que posso chamar qualquer método no objeto String com segurança e não alterar a String passada. Se eu estender o String e alterar a implementação de substring para alterar o String real, o objeto String que você espera que seja imutável não será mais imutável.
Cruncher

1
@Sortofabeginner E assim que você disser que deseja que todos os métodos e campos String sejam finais, apenas para que você possa criar alguma classe com funcionalidades adicionais ... Nesse ponto, é melhor criar uma classe que tenha uma string e crie métodos que operem nessa sequência.
Cruncher

1
O Shay final (entre outras coisas) é usado para tornar um objeto imutável, então eu não diria que eles não têm nada a ver um com o outro. Veja aqui docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

Em Java, itens com o finalmodificador não podem ser alterados!

Isso inclui classes finais, variáveis ​​finais e métodos finais:

  • Uma classe final não pode ser estendida por nenhuma outra classe
  • Uma variável final não pode ser reatribuída outro valor
  • Um método final não pode ser substituído

40
A questão real é por que , não o que .
Francesco Menzani

8
A declaração "Em Java, itens com o finalmodificador não podem ser alterados!", É muito categórica e, de fato, não está totalmente correta. Como disse Grady Booch, "um objeto tem estado, comportamento e identidade". Embora não possamos alterar a identidade de um objeto depois que sua referência foi marcada como final, temos a chance de alterar seu estado atribuindo novos valores aos seus não finalcampos (desde que, é claro, eles os tenham). planejando para obter uma certificação do Oracle Java (como 1Z0-808, etc.) deve manter isso em mente, pois pode haver perguntas sobre este aspecto no exame ...
Igor Soudakevitch

33

Um cenário em que final é importante, quando você deseja impedir a herança de uma classe, por motivos de segurança. Isso permite que você garanta que o código que você está executando não possa ser substituído por alguém.

Outro cenário é para otimização: pareço lembrar que o compilador Java alinha algumas chamadas de função das classes finais. Portanto, se você chamar a.x()e a for declarado final, sabemos em tempo de compilação qual será o código e poderá integrar a função de chamada. Não tenho ideia se isso é realmente feito, mas com final é uma possibilidade.


7
O inlining normalmente é feito apenas pelo compilador just-in-time no tempo de execução. Também funciona sem final, mas o compilador JIT tem um pouco mais de trabalho para garantir que não haja classes de extensão (ou que essas classes de extensão não toquem nesse método).
Pa Elo Ebermann 03/03

Uma boa redação sobre a questão de inlining e otimização pode ser encontrada aqui: lemire.me/blog/archives/2014/12/17/…
Josh Hemann

24

O melhor exemplo é

classe final pública String

que é uma classe imutável e não pode ser estendida. Claro, há mais do que apenas tornar a aula final imutável.


Hehe, às vezes protege os desenvolvedores Rube Goldergian de si mesmos.
Zoidberg

16

Leitura relevante: O Princípio Aberto-Fechado de Bob Martin.

Citação principal:

As entidades de software (classes, módulos, funções etc.) devem estar abertas para extensão, mas fechadas para modificação.

A finalpalavra-chave é o meio de aplicar isso em Java, seja usado em métodos ou em classes.


6
@ Sean: Não declarar finaltorna a classe fechada para extensão em vez de aberta? Ou estou interpretando isso literalmente?
Goran Jovic

4
@Goran globalmente aplicando final, sim. A chave é aplicar seletivamente final em lugares onde você não quer modificação (e, claro, para proporcionar bons ganchos para extensão)
Sean Patrick Floyd

26
No OCP, "modificação" refere-se à modificação do código-fonte e "extensão" refere-se à herança da implementação. Portanto, o uso de finaluma declaração de classe / método não faria sentido se você quiser que o código de implementação seja fechado para modificação, mas aberto para extensão por herança.
Rogério

1
@Rogerio Peguei emprestada a referência (e a interpretação) do Spring Framework Reference (MVC) . IMHO isso faz muito mais sentido do que a versão original.
Sean Patrick Floyd

A extensão está morta. Sem utilidade. Dizimada. Destruído. Eu não me importo com o OCP. Nunca há uma desculpa para estender uma aula.
27518 Josh Woodcock

15

Se você imaginar a hierarquia de classes como uma árvore (como em Java), as classes abstratas podem ser apenas ramificações e as classes finais são aquelas que só podem ser folhas. As classes que se enquadram em nenhuma dessas categorias podem ser ramos e folhas.

Não há violação dos princípios de OO aqui, final é simplesmente fornecer uma simetria agradável.

Na prática, você deseja usar final se quiser que seus objetos sejam imutáveis ​​ou se estiver escrevendo uma API, para sinalizar aos usuários da API que a classe simplesmente não se destina à extensão.


13

A palavra final- chave em si significa que algo é final e não deve ser modificado de forma alguma. Se uma classe estiver marcada final, ela não poderá ser estendida ou subclassificada. Mas a questão é por que marcamos uma classe final? IMO existem várias razões:

  1. Padronização: algumas classes executam funções padrão e não devem ser modificadas, por exemplo, classes que executam várias funções relacionadas à manipulação de strings ou funções matemáticas etc.
  2. Razões de segurança : Às vezes, escrevemos classes que executam várias funções relacionadas a autenticação e senha e não queremos que elas sejam alteradas por mais ninguém.

Ouvi dizer que a classe de marcação finalmelhora a eficiência, mas, francamente, não achei que esse argumento tivesse muito peso.

Se o Java é orientado a objetos e você declara uma classe final, isso não impede a idéia de que a classe tenha as características dos objetos?

Talvez sim, mas às vezes esse é o objetivo pretendido. Às vezes, fazemos isso para obter maiores benefícios de segurança etc., sacrificando a capacidade dessa classe de ser estendida. Mas uma aula final ainda pode estender uma aula, se necessário.

Em uma nota lateral, devemos preferir composição do que herança e a finalpalavra - chave realmente ajuda a aplicar esse princípio.


6

Tenha cuidado ao fazer uma aula "final". Como se você quiser escrever um teste de unidade para uma classe final, não poderá subclassificar essa classe final para usar a técnica de quebra de dependência "Método de subclasse e substituição" descrita no livro de Michael C. Feathers "Trabalhando efetivamente com o código legado" . Neste livro, Feathers disse: "Sério, é fácil acreditar que selado e final é um erro errado, que nunca deveria ter sido adicionado a linguagens de programação. Mas a falha real está conosco. Quando dependemos diretamente de bibliotecas que estão fora de nosso controle, estamos apenas pedindo problemas ".


6

final class pode evitar a quebra da API pública ao adicionar novos métodos

Suponha que na versão 1 da sua Baseclasse você faça:

public class Base {}

e um cliente faz:

class Derived extends Base {
    public int method() { return 1; }
}

Então, se na versão 2 você deseja adicionar um methodmétodo para Base:

class Base {
    public String method() { return null; }
}

isso quebraria o código do cliente.

Se tivéssemos usado final class Base, o cliente não seria capaz de herdar e a adição do método não quebraria a API.


5

Se a classe estiver marcada final, significa que a estrutura da classe não pode ser modificada por nada externo. Onde isso é mais visível, quando você está fazendo uma herança polimórfica tradicional, basicamente class B extends Asimplesmente não funciona. É basicamente uma maneira de proteger algumas partes do seu código (até o ponto) .

Para esclarecer, a marcação de classe finalnão marca seus campos finale, como tal, não protege as propriedades do objeto, mas a estrutura de classe real.


1
O que as propriedades do objeto significam? Isso significa que eu poderia modificar a variável de membro da classe se a classe for declarada final? Portanto, o único objetivo da classe final é impedir a herança.
Adam Lyu

5

PARA ENCONTRAR O PROBLEMA DA CLASSE FINAL:

Existem duas maneiras de fazer uma aula final. O primeiro é usar a palavra-chave final na declaração de classe:

public final class SomeClass {
  //  . . . Class contents
}

A segunda maneira de finalizar uma classe é declarar todos os seus construtores como privados:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Marcá-lo como final evita o problema se descobrir que é realmente um final, para demonstrar o olhar para esta classe de teste. parece público à primeira vista.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Infelizmente, como o único construtor da classe é privado, é impossível estender essa classe. No caso da classe Teste, não há razão para que a classe seja final. A classe Test é um bom exemplo de como as classes finais implícitas podem causar problemas.

Portanto, você deve marcá-lo como final quando implicitamente tornar uma classe final, tornando seu construtor privado.


4

Uma classe final é uma classe que não pode ser estendida. Os métodos também podem ser declarados como finais para indicar que não podem ser substituídos pelas subclasses.

Impedir que a classe seja subclassificada pode ser particularmente útil se você escrever APIs ou bibliotecas e quiser evitar ser estendido para alterar o comportamento base.


4

Uma vantagem de manter uma classe como final:

A classe String é mantida final para que ninguém possa substituir seus métodos e alterar a funcionalidade. por exemplo, ninguém pode alterar a funcionalidade do método length (). Ele sempre retornará o comprimento de uma string.

O desenvolvedor desta classe não queria que ninguém mudasse a funcionalidade dessa classe, então ele a manteve como final.



3

Em java, a palavra-chave final é usada para as ocasiões abaixo.

  1. Variáveis ​​finais
  2. Métodos finais
  3. Aulas Finais

No java, as variáveis ​​finais não podem ser reatribuídas, as classes finais não podem se estender e os métodos finais não podem substituir.


1

As aulas finais não podem ser estendidas. Portanto, se você deseja que uma classe se comporte de uma certa maneira e não substitua os métodos (com código possivelmente menos eficiente e mais malicioso), você pode declarar toda a classe como métodos finais ou específicos que você não deseja que sejam mudou.

Como declarar uma classe não impede que uma classe seja instanciada, isso não significa que impedirá a classe de ter as características de um objeto. É só que você terá que seguir os métodos da maneira como eles são declarados na classe.


1

pense em FINAL como o "fim da linha" - esse cara não pode mais produzir filhos. Portanto, quando você vê dessa maneira, existem vários cenários do mundo real que você encontrará, exigindo que você marque um marcador de 'fim de linha' para a classe. É um design orientado a domínio - se o seu domínio exigir que uma determinada ENTITY (classe) não possa criar subclasses, marque-a como FINAL.

Devo observar que não há nada que o impeça de herdar uma classe "deve ser marcada como final". Mas isso geralmente é classificado como "abuso de herança" e é feito porque na maioria das vezes você deseja herdar alguma função da classe base em sua classe.

A melhor abordagem é examinar o domínio e deixá-lo ditar suas decisões de design.


1

Como dito acima, se você quiser que ninguém possa alterar a funcionalidade do método, poderá declará-lo como final.

Exemplo: caminho do arquivo do servidor de aplicativos para download / upload, sequência de divisão baseada em deslocamento, métodos que você pode declarar como Final para que essas funções do método não sejam alteradas. E se você quiser esses métodos finais em uma classe separada, defina essa classe como Classe final. Portanto, a classe Final terá todos os métodos finais, onde o método Final pode ser declarado e definido na classe não final.



1

Digamos que você tenha uma Employeeclasse que tenha um método greet. Quando o greetmétodo é chamado, ele simplesmente imprime Hello everyone!. Então esse é o comportamento esperado do greetmétodo

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Agora, deixe a GrumpyEmployeesubclasse Employeee substitua o greetmétodo, como mostrado abaixo.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Agora, no código abaixo, dê uma olhada no sayHellométodo Ele toma a Employeeinstância como um parâmetro e chama o método greet, esperando que ele diga Hello everyone!Mas o que obtemos é Get lost!. Essa mudança de comportamento se deve aEmployee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Esta situação pode ser evitada se a Employeeaula foi feita final. Imagine a quantidade de caos que um programador atrevido poderia causar se StringClasse não fosse declarada como final.


1

A aula final não pode ser estendida mais. Se não precisamos tornar uma classe herdável em java, podemos usar essa abordagem.

Se precisarmos fazer com que métodos específicos de uma classe não sejam substituídos, basta colocar a palavra-chave final na frente deles. Lá a classe ainda é herdável.


-1

A Orientação a Objetos não é sobre herança, é sobre encapsulamento. E a herança quebra o encapsulamento.

Declarar uma aula final faz todo o sentido em muitos casos. Qualquer objeto que represente um "valor" como uma cor ou uma quantia em dinheiro pode ser definitivo. Eles estão por conta própria.

Se você estiver escrevendo bibliotecas, finalize suas aulas, a menos que você as indente explicitamente como derivadas. Caso contrário, as pessoas podem derivar suas classes e substituir métodos, quebrando suas suposições / invariantes. Isso também pode ter implicações de segurança.

Joshua Bloch em “Java Efetivo” recomenda projetar explicitamente para herança ou proibi-la e ele observa que projetar para herança não é tão fácil.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.