Herança
Nas lições anteriores, você viu a herança mencionada várias vezes. Na linguagem Java, as classes podem ser derivadas de outras classes, herdando, assim, campos e métodos dessas classes.
Com exceção de
Object
, que não tem superclasse, cada classe tem uma e apenas uma superclasse direta (herança única). Na ausência de qualquer outra superclasse explícita, cada classe é implicitamente uma subclasse de Object
. As classes podem ser derivadas de classes que são derivadas de classes que são derivadas de classes, e assim por diante e, finalmente, derivado da classe superior,
Object
. Diz-se que essa classe descende de todas as classes na cadeia de herança que remonta a Object
. A ideia de herança é simples, mas poderosa: quando você deseja criar uma nova classe e já existe uma classe que inclui parte do código que você deseja, você pode derivar sua nova classe da classe existente . Ao fazer isso, você pode reutilizar os campos e métodos da classe existente sem ter que escrevê-los (e depurá-los!) Você mesmo.
Uma subclasse herda todos os membros (campos, métodos e classes aninhadas) de sua superclasse. Construtores não são membros, portanto não são herdados por subclasses, mas o construtor da superclasse pode ser invocado a partir da subclasse.
A hierarquia de classes da plataforma Java
O Object
, definida no pacote java.lang
, define e implementa o comportamento comum a todas as classes – incluindo aquelas que você escreve. Na plataforma Java, muitas classes derivam diretamente de Object
, outras classes derivam de algumas dessas classes e assim por diante, formando uma hierarquia de classes.
Todas as classes na plataforma Java são descendentes do objeto
No topo da hierarquia, Object
é a mais geral de todas as classes. As classes próximas à parte inferior da hierarquia fornecem um comportamento mais especializado.
Um exemplo de herança
Aqui está o código de amostra para uma possível implementação de um Bicycle
classe que foi apresentada na lição Classes e objetos:
Uma declaração de classe para uma MountainBike
classe que é uma subclasse de Bicycle
pode ter esta aparência:
public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
herda todos os campos e métodos de Bicycle
e adiciona o campo seatHeight
e um método para defini-lo. Exceto pelo construtor, é como se você tivesse escrito uma nova MountainBike
classe inteiramente do zero, com quatro campos e cinco métodos. No entanto, você não precisava fazer todo o trabalho. Isso seria especialmente valioso se os métodos na classe Bicycle
fossem complexos e levassem muito tempo para depurar.
O que você pode fazer em uma subclasse
Uma subclasse herda todos os membros públicos e protegidos de seu pai, não importa em qual pacote a subclasse esteja. Se a subclasse estiver no mesmo pacote que seu pai, ele também herda os membros privados do pacote do pai. Você pode usar os membros herdados como estão, substituí-los, ocultá-los ou complementá-los com novos membros:
- Os campos herdados podem ser usado diretamente, como qualquer outro campo.
- Você pode declarar um campo na subclasse com o mesmo nome que o da superclasse, ocultando-o assim (não recomendado).
- Você pode declarar novos campos na subclasse que não estão na superclasse.
- Os métodos herdados podem ser usados diretamente como são.
- Você pode escrever um novo método de instância no subclasse que tem a mesma assinatura da superclasse, substituindo-a, portanto.
- Você pode escrever um novo método estático na subclasse que tenha a mesma assinatura da superclasse, ocultando-o.
- Você pode declarar novos métodos na subclasse que não estão na superclasse.
- Você pode escrever um construtor de subclasse que invoca o construtor da superclasse, implicitamente ou usando a palavra-chave
super
.
As seções a seguir nesta lição irão expandir esses tópicos.
Membros privados em uma superclasse
Uma subclasse não herda os private
membros de sua classe pai. No entanto, se a superclasse tiver métodos públicos ou protegidos para acessar seus campos privados, eles também podem ser usados pela subclasse.
Uma classe aninhada tem acesso a todos os membros privados de sua classe envolvente – tanto campos quanto métodos. Portanto, uma classe aninhada pública ou protegida herdada por uma subclasse tem acesso indireto a todos os membros privados da superclasse.
Casting Objetos
Vimos que um objeto é do tipo de dados da classe da qual foi instanciado. Por exemplo, se escrevermos
public MountainBike myBike = new MountainBike();
, então myBike
é do tipo MountainBike
.
MountainBike
é descendente de Bicycle
e Object
. Portanto, um MountainBike
é um Bicycle
e também é um Object
, e pode ser usado sempre que objetos Bicycle
ou Object
são chamados.
O inverso não é necessariamente verdadeiro: um Bicycle
pode ser um MountainBike
, mas não é necessariamente. Da mesma forma, um Object
pode ser um Bicycle
ou um MountainBike
, mas não é necessariamente.
Casting mostra o uso de um objeto de um tipo no lugar de outro tipo, entre os objetos permitidos por herança e implementações. Por exemplo, se escrevermos
Object obj = new MountainBike();
então obj
é um Object
e um MountainBike
(até que obj
seja atribuído outro objeto que não seja um MountainBike
). Isso é chamado de conversão implícita.
Se, por outro lado, escrevermos
MountainBike myBike = obj;
, nós obtém um erro de tempo de compilação porque obj
não é conhecido pelo compilador como um MountainBike
. No entanto, podemos dizer ao compilador que prometemos atribuir um MountainBike
a obj
por conversão explícita:
MountainBike myBike = (MountainBike)obj;
Este elenco insere uma verificação de tempo de execução que obj
está atribuído a um MountainBike
para que o compilador possa assumir com segurança que obj
é um MountainBike
. Se obj
não for um MountainBike
em tempo de execução, uma exceção será lançada.
instanceof
. Isso pode salvá-lo de um erro de tempo de execução devido a um elenco impróprio. Por exemplo:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj;}
Aqui, o operador instanceof
verifica se obj
refere-se a um MountainBike
para que possamos fazer o elenco com o conhecimento de que não haverá exceção de tempo de execução lançada.