1. Datilografia

As variáveis ​​que armazenam tipos de referência (classes) também podem ser convertidas em tipos diferentes. Mas isso só funciona dentro de uma única hierarquia de tipo. Vejamos um exemplo simples. Suponha que tenhamos a seguinte hierarquia de classes, na qual as classes abaixo herdam as classes acima.

Datilografia

Typecasting de tipos de referência, bem como os primitivos, também é categorizado como alargamento e estreitamento.

Vemos que a classe Cat herda a classe Pet, e a classe Pet, por sua vez, herda a classe Animal.

Se escrevermos um código como este:

Animal kitten = new Cat();

Esta é uma conversão de tipo de alargamento . Também é chamado de conversão implícita. Ampliamos a referência de gato para que ela agora se refira a um objeto Gato . Com uma conversão de tipo como essa, não poderemos usar a referência do gatinho para chamar métodos que estão presentes na classe Cat , mas ausentes na classe Animal .

Uma conversão estreita (ou elenco explícito) acontece na direção oposta:

Cat cat = (Cat) kitten;

Indicamos explicitamente que queremos converter a referência armazenada na variável gatinho (cujo tipo é Animal ) para o tipo Gato .



2. Verificando o tipo de um objeto

Mas você precisa ter muito cuidado aqui. Se você fizer isto:

Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;

O compilador permitirá esse código, mas haverá um erro quando o programa for executado! A JVM lançará uma exceção:

Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf

As referências a um objeto Cat só podem ser armazenadas em variáveis ​​cujo tipo é um ancestral da classe Cat: Pet, Animal ou Object.

Por que é que?

O ponto relevante aqui é que uma referência de objeto é usada para se referir aos métodos e variáveis ​​desse objeto . E não haverá problemas se usarmos uma variável Animal para armazenar uma referência a um objeto Cat: o tipo Cat sempre tem variáveis ​​e métodos do tipo Animal — ele os herdou!

Mas se a JVM nos permitisse armazenar uma referência a um objeto Cat em uma variável Wolf, poderíamos ter uma situação em que poderíamos tentar usar a variável grayWolf para chamar um método que não existe no objeto Cat armazenado nessa variável . É por isso que esse arranjo não é permitido.

Java possui um operador especial instanceofque permite verificar se um objeto é de um determinado tipo e, portanto, pode ser armazenado em uma variável de determinado tipo. Parece bem simples:

variable instanceof Type

Exemplo:

Animal beast = new Cat();
if (beast instanceof Wolf)
{
   Wolf grayWolf = (Wolf) beast;
}

Este código não causará erros — mesmo em tempo de execução.

Aqui estão mais alguns exemplos que ilustram a situação:

Ampliação da conversão do tipo Descrição
Cow cow = new Whale();

Esta é uma conversão de alargamento clássica — nenhum operador de conversão de tipo é necessário. Agora apenas os métodos definidos na Cowclasse podem ser chamados no Whaleobjeto.

Na cowvariável , o compilador só vai deixar você chamar métodos que seu tipo (a Cowclasse) possui.

Conversão de tipo de restrição
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Conversão de restrição clássica: você precisa adicionar uma verificação de tipo e um operador de conversão.
A Cow cowvariável armazena uma referência a um Whaleobjeto.
Verificamos se esse é o caso e, em seguida, realizamos uma conversão de tipo (estreitamento). Ou como também é chamado:
um elenco de tipo
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // Exception
Você pode restringir um tipo de referência sem verificar o tipo do objeto.
Se a cowvariável se referir a um objeto que não seja um , será gerado Whaleum .InvalidClassCastException


3. Chamando o método original: a superpalavra-chave

Ao substituir o método de uma classe pai, às vezes, em vez de substituí-lo pelo nosso, queremos apenas complementá-lo ligeiramente.

Seria legal se pudéssemos colocar o método da classe pai em nosso método e então executar parte do nosso próprio código. Ou talvez primeiro execute nosso próprio código e, em seguida, chame o método da classe pai.

E Java nos permite fazer exatamente isso. Para chamar um método da classe pai, faça o seguinte:

super.method(arguments);

Exemplos:

class PeaceTime
{
   public double getPi()
   {
      return 3.14;
   }
}

class WarTime extends PeaceTime
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

Em tempo de guerra, o valor de Pipode ser maior que 6! Claro, estamos brincando, mas este exemplo demonstra como tudo isso pode funcionar.

Aqui estão mais alguns exemplos para esclarecer um pouco as coisas:

Código Descrição
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("This is incorrect: ");
      super.printName();
      System.out.println("I'm a whale");
   }
}
Cowe Whaleaulas
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
A saída da tela será:
I'm a white whale
This is incorrect: I'm a cow
I'm a whale

Isso é difícil. Honestamente, é uma das coisas mais difíceis em OOP . Dito isto, você precisa conhecê-lo e entendê-lo.