Oi! Nas aulas anteriores, conhecemos as interfaces e descobrimos para que servem. O tópico de hoje ecoará o anterior. Vamos falar sobre classes abstratas em Java.
Por que as classes são chamadas de 'abstratas'
Você provavelmente se lembra do que é 'abstração' - já falamos sobre isso. :) Se você esqueceu, não tenha medo. Lembre-se: é um princípio da POO que diz que, ao projetar classes e criar objetos, devemos identificar apenas as propriedades principais da entidade e descartar as secundárias. Por exemplo, se estamos projetando umaSchoolTeacher
classe, dificilmente precisamos de uma propriedade ' height '. De fato, essa propriedade é irrelevante para um professor. Mas se estivermos criando uma BasketballPlayer
classe, o crescimento seria uma característica importante. Então ouça. Uma classe abstrataé o mais abstrato possível — um 'branco' inacabado para um grupo de classes futuras. O espaço em branco não pode ser usado como está. É muito 'cru'. Mas descreve certo estado e comportamento geral que serão possuídos por classes futuras que herdam a classe abstrata.
Exemplos de classes Java abstratas
Considere um exemplo simples com carros:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public abstract void gas();
public abstract void brake();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
É assim que a classe abstrata mais simples se parece. Como você pode ver, não é nada de especial :) Por que precisaríamos disso? Em primeiro lugar, descreve nossa entidade necessária, um carro, da maneira mais abstrata possível. Há uma razão pela qual estamos usando a palavra abstract . No mundo real, não existem 'carros abstratos'. Há caminhões, carros de corrida, sedãs, cupês e SUVs. Nossa classe abstrata é simplesmente um 'plano' que usaremos mais tarde para criar classes de carros.
public class Sedan extends Car {
@Override
public void gas() {
System.out.println("The sedan is accelerating!");
}
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
Isso é muito semelhante ao que falamos nas lições sobre herança. Mas nessas aulas, tínhamos uma classe Car e seus métodos não eram abstratos. Mas essa solução tem várias desvantagens que são corrigidas em classes abstratas. Em primeiro lugar, você não pode criar uma instância de uma classe abstrata :
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Error! The Car class is abstract!
}
}
Os criadores do Java projetaram especificamente esse 'recurso'. Mais uma vez, como lembrete: uma classe abstrata é apenas um modelo para futuras classes 'normais' . Você não precisa de cópias do projeto, certo? E você não cria instâncias de uma classe abstrata :) Mas se a Car
classe não fosse abstrata, poderíamos facilmente criar instâncias dela:
public class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
// Some logic
}
public void brake() {
// Some logic
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Everything is fine. A car is created.
}
}
Agora, nosso programa tem algum tipo de carro incompreensível - não é um caminhão, nem um carro de corrida, nem um sedã, e não está totalmente claro o que é. Este é o próprio 'carro abstrato' que não existe na natureza. Podemos fornecer o mesmo exemplo usando animais. Imagine se Animal
classes ( animais abstratos ). Não está claro que tipo de animal é, a que família pertence e que características possui. Seria estranho ver isso em seu programa. Não existem 'animais abstratos' na natureza. Apenas cachorros, gatos, raposas, toupeiras, etc. Classes abstratas nos livram de objetos abstratos. Eles nos dão estado e comportamento básicos. Por exemplo, todos os carros devem ter um modelo , cor e velocidade máxima , e você deve ser capaz de aplicar ogás e freio . É isso. Este é um plano abstrato geral. Em seguida, você projeta as classes necessárias. Nota: dois métodos na classe abstract também são designados como abstract , e eles não possuem nenhuma implementação. A razão é a mesma: classes abstratas não criam comportamento padrão para carros abstratos. Eles apenas indicam o que todo carro deve ser capaz de fazer. No entanto, se você precisar de um comportamento padrão, poderá implementar métodos em uma classe abstrata. Java não proíbe isso:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
System.out.println("Gas!");
}
public abstract void brake();
// Getters and setters
}
public class Sedan extends Car {
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
sedan.gas();
}
}
Saída do console: "Gás!" Como você pode ver, implementamos o primeiro método na classe abstrata, e não o segundo. Como resultado, o Sedan
comportamento da nossa classe é dividido em duas partes: se você chamar o gas()
método, a chamada 'sobe' até a Car
classe pai abstrata, mas sobrescrevemos o brake()
método na Sedan
classe. Isso acaba sendo muito conveniente e flexível. Mas agora nossa classe não é tão abstrata ? Afinal, metade de seus métodos são implementados. Isso é realmente uma característica muito importante - uma classe é abstrata se pelo menos um de seus métodos for abstrato. Um dos dois métodos, ou pelo menos um dos mil métodos - não faz diferença. Podemos até implementar todos os métodos e não deixar nenhum deles abstrato. Então seria uma classe abstrata sem métodos abstratos. Em princípio, isso é possível, e o compilador não gerará erros, mas é melhor evitar: A palavra abstract perde o significado e seus colegas programadores ficarão muito surpresos :/ Ao mesmo tempo, se um método for marcado com a palavra abstract, cada classe filha deve implementá-la ou declará-la como abstract. Caso contrário, o compilador gerará um erro. Claro, cada classe pode herdar apenas uma classe abstrata, então em termos de herança não há diferença entre classes abstratas e ordinárias. Não importa se herdamos uma classe abstrata ou comum, pode haver apenas uma classe pai.
Por que Java não tem herança múltipla de classes
Já dissemos que Java não tem herança múltipla, mas ainda não exploramos o porquê. Vamos tentar fazer isso agora. O fato é que se Java tivesse herança múltipla, as classes filhas não seriam capazes de decidir qual comportamento específico deveriam escolher. Suponha que temos duas classes -Toaster
e NuclearBomb
:
public class Toaster {
public void on() {
System.out.println("The toaster is on. Toast is being prepared!");
}
public void off() {
System.out.println("The toaster is off!");
}
}
public class NuclearBomb {
public void on() {
System.out.println("Boom!");
}
}
Como você pode ver, ambos têm um on()
método. Para uma torradeira, começa a torrar. Para uma bomba nuclear, desencadeia uma explosão. Ops: / Agora imagine que você decidiu (não me pergunte por quê!) Criar algo intermediário. E assim você tem uma MysteriousDevice
aula! Este código, é claro, não funciona, e nós o fornecemos apenas como um exemplo 'mas poderia ser':
public class MysteriousDevice extends Toaster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // So what should happen here? Do we get toast or a nuclear apocalypse?
}
}
Vamos dar uma olhada no que temos. O misterioso dispositivo herda simultaneamente a Torradeira e a Bomba Nuclear. Ambos têm on()
métodos. Como resultado, se chamarmos o on()
método, não fica claro qual deles deve ser invocado no MysteriousDevice
objeto. Não há como o objeto saber. E ainda por cima: a NuclearBomb não tem um off()
método, então se não acertarmos, será impossível desabilitar o dispositivo. É justamente por causa dessa 'confusão', onde o objeto não sabe que comportamento exibir, que os criadores de Java abandonaram a herança múltipla. No entanto, você deve se lembrar de que as classes Java podem implementar várias interfaces. A propósito, em seus estudos, você já encontrou pelo menos uma classe abstrata!
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
É seu velho amigo, a Calendar
classe. É abstrato e tem vários filhos. Um deles é GregorianCalendar
. Você já o usou nas aulas sobre datas. :) Tudo parece claro o suficiente. Há apenas uma pergunta: afinal, qual é a diferença fundamental entre classes abstratas e interfaces? Por que eles adicionaram ambos ao Java em vez de apenas limitar o idioma a um? Afinal, isso teria sido inteiramente adequado. Falaremos sobre isso na próxima aula ! Até então :)
GO TO FULL VERSION