Oi! Hoje vamos dar uma olhada em um princípio da Programação Orientada a Objetos (OOP): Herança. Também estudaremos outros tipos de relacionamentos entre classes: composição e agregação.
Este tópico não será difícil: você já encontrou herança e exemplos de herança muitas vezes em lições anteriores. Hoje, o principal será reforçar seus conhecimentos, examinar o mecanismo de herança com mais detalhes e, mais uma vez, repassar alguns exemplos. :) Bem, vamos!
Quando temos algumas dezenas de classes de carros, a quantidade de código duplicado torna-se realmente séria. Mover campos e métodos comuns (também chamados de "estados" e "comportamentos") para uma classe pai nos permite economizar muito tempo e espaço. Se algum tipo tiver propriedades ou métodos únicos que outros tipos de carros não possuem, não é grande coisa. Você sempre pode criá-los em uma classe descendente, separada de todos os outros.
O mesmo vale para os campos: se uma classe filha tiver propriedades únicas, declaramos calmamente esses campos dentro da classe filha e paramos de nos preocupar. :) A capacidade de reutilizar código é a principal vantagem da herança. Para programadores, é muito importante não escrever código extra. Você encontrará isso repetidamente em seu trabalho. Por favor, lembre-se de outra coisa crucial: Java não tem herança múltipla. Cada classe herda apenas uma classe. Falaremos mais sobre as razões para isso em lições futuras. Por enquanto, apenas lembre-se disso. A propósito, isso torna o Java diferente de algumas outras linguagens OOP. Por exemplo, C++ suporta herança múltipla. Tudo é mais ou menos claro com herança. Vamos continuar.

Herança em Java e suas vantagens
Como você certamente se lembra, a herança é um mecanismo que permite descrever uma nova classe com base em uma classe existente (classe pai). Ao fazer isso, a nova classe empresta as propriedades e a funcionalidade da classe pai. Vamos relembrar um exemplo de herança dado nas lições anteriores:
public class Car {
private String model;
private int maxSpeed;
private int yearOfManufacture;
public Car(String model, int maxSpeed, int yearOfManufacture) {
this.model = model;
this.maxSpeed = maxSpeed;
this.yearOfManufacture = yearOfManufacture;
}
public void gas() {
// Gas
}
public void brake() {
// Brake
}
}
public class Truck extends Car {
public Truck(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
public class Sedan extends Car {
public Sedan(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
Temos um determinado programa que envolve trabalhar com vários tipos de carros. Mesmo que você não seja um entusiasta de carros, provavelmente sabe que existem muitos tipos de carros no mundo. :) Da mesma forma, vamos separar as propriedades comuns dos carros em uma classe pai comum chamada Car
. Então, o que é comum a todos os carros, independentemente do seu tipo? Cada carro tem um ano de fabricação, nome do modelo e velocidade máxima. Colocamos essas propriedades nos campos model
, maxSpeed
e yearOfManufacture
. Quanto ao comportamento, qualquer carro pode acelerar e desacelerar. :) Definimos esse comportamento no gas()
ebrake()
métodos. Que benefícios isso nos traz? Em primeiro lugar, reduz a quantidade de código. Claro, podemos fazer sem a classe pai. Mas como cada carro deve ser capaz de acelerar e desacelerar, teremos que criar métodos gas()
e brake()
nas classes Truck
, Sedan
, F1Car
e SportsCar
e em todas as outras classes de carros. Imagine quanto código extra teríamos que escrever. E não se esqueça dos campos model
, maxSpeed
, e yearOfManufacture
: se nos livrarmos da classe pai, teremos que criá-los em cada classe de carro! 
public class F1Car extends Car {
public void pitStop() {
// Only race cars make pit stops
}
public static void main(String[] args) {
F1Car formula1Car = new F1Car();
formula1Car.gas();
formula1Car.pitStop();
formula1Car.brake();
}
}
Vejamos os carros de corrida de Fórmula 1 como exemplo. Ao contrário de seus "parentes", eles têm um comportamento único - eles param nas boxes de vez em quando. Isso não nos incomoda. Já descrevemos o comportamento comum na Car
classe pai, e o comportamento específico das classes descendentes pode ser adicionado a essas classes. 
Composição e agregação
Classes e objetos podem ser vinculados. A herança descreve um relacionamento "é-um". Um leão é um animal. Tal relacionamento é facilmente expresso usando herança, ondeAnimal
é a classe pai e Lion
é a filha. No entanto, nem todos os relacionamentos são descritos dessa maneira. Por exemplo, um teclado está definitivamente relacionado a um computador, mas não é um computador . As mãos estão de alguma forma relacionadas a uma pessoa, mas não são uma pessoa. Nesses casos, temos outro tipo de relação: não "é-um", mas "tem-um". Uma mão não é uma pessoa, mas faz parte de uma pessoa. Um teclado não é um computador, mas faz parte de um computador. Um relacionamento tem-um pode ser descrito em código usando composição e agregação. A diferença está no "rigidez" da relação. Vamos dar um exemplo simples: Temos uma Car
classe. Todo carro tem um motor. Além disso, todo carro tem passageiros. Qual é a diferença fundamental entre os campos Engine engine
e Passenger[] passengers
? O fato de o passageiro A
estar sentado no carro não significa que os passageiros B
não C
estejam no carro. Um carro pode corresponder a vários passageiros. Além do mais, se todos os passageiros saírem de um carro, ele ainda funcionará sem problemas. A relação entre a Car
classe e o Passenger[] passengers
array é menos estrita. Chama-se agregação . Ele fornece outro bom exemplo de agregação. Suponha que temos uma Student
classe e umStudentGroup
aula. Um aluno pode ingressar em várias organizações estudantis: um clube de física, um fã-clube de Star Wars e/ou um clube de comédia estudantil. A composição é um tipo mais estrito de relacionamento. Ao usar composição, um objeto faz parte de algum objeto e não pode pertencer a outro objeto do mesmo tipo. O exemplo mais simples é o motor de um carro. Um motor é parte de um carro e não pode ser parte de outro carro. Como você pode ver, o relacionamento deles é muito mais rígido do que o relacionamento entre Car
e Passengers
. 
GO TO FULL VERSION