Java oferece suporte à reutilização de classes por meio de herança e composição. Este tutorial de duas partes ensina como usar herança em seus programas Java.

O que você aprenderá neste tutorial Java

A primeira metade desta introdução à herança Java ensina como usar o extends palavra-chave para derivar uma classe filha de uma classe pai, invocar construtores e métodos da classe pai e substituir métodos:

  • O que é herança em Java?
  • Herança única e herança múltipla
  • Como usar a palavra-chave extends em Java
  • Compreendendo a hierarquia de classes Java
  • Quando usar substituição de método versus sobrecarga de método
download

Baixe o código-fonte de aplicativos de exemplo neste tutorial. Criado por Jeff Friesen.

O que é herança em Java?

Herança é uma construção de programação que os desenvolvedores de software usam para estabelecer é um relacionamento entre categorias. A herança nos permite derivar categorias mais específicas de outras mais genéricas. A categoria mais específica é um tipo de categoria mais genérica. Por exemplo, uma conta corrente é um tipo de conta na qual você pode fazer depósitos e saques. Da mesma forma, um caminhão é um tipo de veículo usado para transportar itens grandes.

A herança pode passar por vários níveis, levando a categorias cada vez mais específicas. Como exemplo, a Figura 1 mostra carro e caminhão herdados de veículo; perua herdada de carro; e caminhão de lixo herdado de caminhão. As setas apontam de categorias “filhos” mais específicas (em baixo) para categorias “pais” menos específicas (em cima).

Figura 1. Um par de hierarquias de herança está enraizado na categoria de veículo comum

Herança única e herança múltipla

O exemplo na Figura 1 ilustra herança única em que uma categoria filha herda estados e comportamentos de uma categoria pai imediata. Em contraste, herança múltipla permite que uma categoria filha herde estados e comportamentos de duas ou mais categorias pai imediatas. A hierarquia na Figura 2 ilustra a herança múltipla.

herança jwp1 fig2

Figura 2. Hovercraft multiplica heranças das categorias de veículos terrestres e aquáticos

As categorias são descritas por classes. Java suporta herança única por meio de extensão de classe, em que uma classe herda diretamente campos e métodos acessíveis de outra classe, estendendo essa classe. Entretanto, Java não suporta herança múltipla por meio de extensão de classe.

Ao visualizar uma hierarquia de herança, você pode detectar facilmente herança múltipla pela presença de um padrão de losango. A Figura 2 mostra esse padrão no contexto de veículo, veículo terrestre, veículo aquático e hovercraft.

Como usar a palavra-chave extends em Java

Java suporta extensão de classe através do extends palavra-chave. Quando presente, extends especifica um relacionamento pai-filho entre duas classes. Abaixo eu uso extends estabelecer uma relação entre as classes Vehicle e Care depois entre Account e SavingsAccount:

Listagem 1. A palavra-chave 'extends' especifica um relacionamento pai-filho

class Vehicle
{
   // member declarations
}
class Car extends Vehicle
{
   // inherit accessible members from Vehicle
   // provide own member declarations
}
class Account
{
   // member declarations
}
class SavingsAccount extends Account
{
   // inherit accessible members from Account
   // provide own member declarations
}

O extends palavra-chave é especificada após o nome da classe e antes de outro nome de classe. O nome da classe antes extends identifica o filho e o nome da classe após extends identifica o pai. É impossível especificar vários nomes de classes depois extends porque Java não suporta herança múltipla baseada em classe.

Estes exemplos codificam relacionamentos é-a: Car é um especializado Vehicle e SavingsAccount é um especializado Account. Vehicle e Account são conhecidos como classes básicas, classes parentaisou superclasses. Car e SavingsAccount são conhecidos como classes derivadas, aulas infantisou subclasses.

As classes filhas herdam campos e métodos acessíveis de suas classes pai e de outros ancestrais. Eles nunca herdam construtores, entretanto. Em vez disso, as classes filhas declaram seus próprios construtores. Além disso, eles podem declarar seus próprios campos e métodos para diferenciá-los de seus pais. Considere a Listagem 2.

Listagem 2. Uma classe pai Account

class Account
{
   private String name;

   private long amount;

   Account(String name, long amount)
   {
      this.name = name;
      setAmount(amount);
   }

   void deposit(long amount)
   {
      this.amount += amount;
   }

   String getName()
   {
      return name;
   }

   long getAmount()
   {
      return amount;
   }

   void setAmount(long amount)
   {
      this.amount = amount;
   }
}

A Listagem 2 descreve uma classe genérica de conta bancária que possui um nome e um valor inicial, ambos definidos no construtor. Além disso, permite que os usuários façam depósitos. (Você pode fazer saques depositando quantias negativas de dinheiro, mas ignoraremos essa possibilidade.) Observe que o nome da conta deve ser definido quando uma conta é criada.

A Listagem 3 apresenta um SavingsAccount classe filha que estende seu Account classe pai.

Listagem 3. Uma classe filha SavingsAccount estende sua classe pai Account

class SavingsAccount extends Account
{
   SavingsAccount(long amount)
   {
      super("savings", amount);
   }
}

O SavingsAccount class é trivial porque não precisa declarar campos ou métodos adicionais. No entanto, ele declara um construtor que inicializa os campos em seu Account superclasse. A inicialização acontece quando AccountO construtor de é chamado via Java super palavra-chave, seguida por uma lista de argumentos entre parênteses.

A Listagem 4 estende ainda mais Account com um CheckingAccount aula.

Listagem 4. Uma classe filha CheckingAccount estende sua classe pai Account

class CheckingAccount extends Account
{
   CheckingAccount(long amount)
   {
      super("checking", amount);
   }

   void withdraw(long amount)
   {
      setAmount(getAmount() - amount);
   }
}

CheckingAccount é um pouco mais substancial do que SavingsAccount porque declara um withdraw() método. Observe as chamadas deste método para setAmount() e getAmount()qual CheckingAccount herda de Account. Você não pode acessar diretamente o amount campo em Account porque este campo é declarado private (veja Listagem 2).

Compreendendo a hierarquia de classes Java

Eu criei um AccountDemo classe de aplicativo que permite experimentar o Account hierarquia de classes. Primeiro, dê uma olhada AccountDemocódigo-fonte de.

Listagem 5. AccountDemo demonstra a hierarquia de classes de conta

class AccountDemo
{
   public static void main(String() args)
   {
      SavingsAccount sa = new SavingsAccount(10000);
      System.out.println("account name: " + sa.getName());
      System.out.println("initial amount: " + sa.getAmount());
      sa.deposit(5000);
      System.out.println("new amount after deposit: " + sa.getAmount());

      CheckingAccount ca = new CheckingAccount(20000);
      System.out.println("account name: " + ca.getName());
      System.out.println("initial amount: " + ca.getAmount());
      ca.deposit(6000);
      System.out.println("new amount after deposit: " + ca.getAmount());
      ca.withdraw(3000);
      System.out.println("new amount after withdrawal: " + ca.getAmount());
   }
}

O main() método na Listagem 5 demonstra primeiro SavingsAccountentão CheckingAccount. Supondo Account.java, SavingsAccount.java, CheckingAccount.javae AccountDemo.java arquivos de origem estão no mesmo diretório, execute um dos seguintes comandos para compilar todos esses arquivos de origem:

javac AccountDemo.java
javac *.java

Execute o seguinte comando para executar o aplicativo:

java AccountDemo

Você deve observar a seguinte saída:

account name: savings
initial amount: 10000
new amount after deposit: 15000
account name: checking
initial amount: 20000
new amount after deposit: 26000
new amount after withdrawal: 23000

Substituição de método vs. sobrecarga de método

Uma subclasse pode sobrepor (substituir) um método herdado para que a versão do método da subclasse seja chamada. Um método de substituição deve especificar o mesmo nome, lista de parâmetros e tipo de retorno que o método que está sendo substituído. Para demonstrar, declarei um print() método no Vehicle classe, mostrada na Listagem 6.