- Aprender
- Computação e TI
- Programação
- Programação orientada a objetos com Java
- Herança, interfaces e polimorfismo em Java
Herança, interfaces e polimorfismo em Java
extends e super, sobrescreva métodos herdados e forneça às suas classes métodos toString, equals e hashCode úteis de Object. Defina comportamentos compartilhados com classes abstratas e interfaces, preveja qual método será executado por meio de polimorfismo e vinculação dinâmica e modele opções fixas e dados simples com enums e records. Exemplos de código comentados mostram cada etapa e os erros mais comuns de iniciantes.BásicoAula 8 de 11 do curso Curso de Programação orientada a objetos com Java
O que você vai aprender
- Construa uma nova classe a partir de uma existente com
extends, identifique o que a nova classe herda e garanta que a parte herdada de cada objeto seja inicializada corretamente comsuper.- Crie uma subclasse com extendsDeclare uma subclasse, identifique quais membros ela herda e pode usar e decida, com o teste “é um”, se a herança é adequada.
- Inicialize objetos com construtores da superclassePasse valores ao construtor da superclasse com
super(...), preveja a ordem de execução dos construtores e evite os erros clássicos com construtores.
- Crie uma subclasse com extends
- Altere o comportamento herdado sobrescrevendo métodos, amplie a versão da superclasse com
supere forneça às suas classes métodostoString,equalsehashCodesignificativos deObject.- Sobrescreva métodos herdadosSubstitua ou amplie um método herdado com uma sobrescrita correta, chame a versão da superclasse com
supere distinga sobrescrita de sobrecarga. - Use e sobrescreva os métodos de ObjectExplique por que todo objeto tem
toString,equals,hashCodeegetClass, interprete seu comportamento padrão e escreva umtoStringútil. - Sobrescreva equals e hashCode corretamenteDefina quando dois objetos são iguais com um
equals(Object)correto, mantenhahashCodeconsistente com ele e trate a igualdade em uma hierarquia de classes.
- Sobrescreva métodos herdados
- Use classes abstratas para compartilhar código entre classes estreitamente relacionadas, use interfaces para estabelecer um compromisso de comportamento entre classes sem relação entre si e escolha a ferramenta adequada para um projeto.
- Escreva classes abstratas e métodos abstratosDeclare classes e métodos abstratos, complete-os em subclasses concretas e use uma classe abstrata para compartilhar campos e código.
- Defina e implemente interfacesEscreva uma interface como contrato, implemente-a em uma ou mais classes e combine interfaces com herança.
- Adicione métodos padrão, estáticos e privados a interfacesForneça comportamento compartilhado a interfaces com métodos padrão, acrescente métodos auxiliares com métodos estáticos e privados e resolva conflitos entre métodos padrão.
- Escolha entre uma classe abstrata e uma interfaceCompare classes abstratas e interfaces e escolha a opção adequada, ou ambas em conjunto, para um projeto.
- Escreva classes abstratas e métodos abstratos
- Referencie objetos por tipos gerais, preveja qual método será executado em tempo de execução, converta tipos com segurança e escreva código que continue funcionando quando novas subclasses forem acrescentadas.
- Referencie objetos por tipos de superclasse e de interfaceDistinga o tipo declarado de uma variável do tipo real do objeto e use tipos de superclasse e de interface para variáveis, parâmetros e listas.
- Preveja qual método será executado com vinculação dinâmicaAcompanhe chamadas de métodos em uma hierarquia de classes, distinguindo o que o compilador decide do que é decidido em tempo de execução.
- Converta entre tipos com conversões explícitas e instanceofFaça conversões para subtipo com segurança usando
instanceofe correspondência de padrões, reconheçaClassCastExceptione identifique quando um teste de tipo sinaliza a falta de um método sobrescrito. - Projete código flexível com polimorfismoEscreva métodos e coleções usando tipos gerais para que novas classes possam ser usadas sem alterações e torne classes ordenáveis com
Comparable.
- Referencie objetos por tipos de superclasse e de interface
- Represente um conjunto fixo de opções com um enum e um dado simples com um record, usando os membros que o compilador gera para você.
- Represente conjuntos fixos de valores com enumsDeclare enums, use seus métodos integrados e
switche forneça campos, construtores e comportamento às constantes de enumeração. - Modele dados com recordsDeclare records para dados simples, use o construtor gerado, os métodos de acesso,
equals,hashCodeetoStringe valide dados em um construtor compacto.
- Represente conjuntos fixos de valores com enums
Guia de estudo de Herança, interfaces e polimorfismo em Java
1Estenda classes com herança
Uma subclasse começa com os campos e o comportamento de uma classe existente e acrescenta o que a diferencia. Essa relação afeta tanto a forma como outros códigos podem usar o objeto quanto a forma como Java o constrói. Acompanhe uma pessoa e um estudante em suas declarações e construtores: os dados privados permanecem sob o controle da superclasse, e as chamadas de construtores fornecem os valores necessários ao estado herdado.
1.1 Crie uma subclasse e proteja seus dados
A herança cria uma subclasse a partir de uma superclasse existente. Em class Student extends Person, todo estudante é uma pessoa. Um objeto estudante reúne seu estado de pessoa e seu estado adicional de estudante em um único objeto.
Acrescente as partes específicas do estudante
Aqui estão uma pequena superclasse e uma subclasse:
class Person {
private String name;
public Person(String name) { this.name = name; }
public String getName() { return name; }
}
class Student extends Person {
private int credits;
public Student(String name) { super(name); }
public void study() { credits += 5; }
public int getCredits() { return credits; }
}
Student herda getName(). Ela acrescenta um campo de créditos e dois métodos. Após Student s = new Student("Ada Lovelace");, o campo de créditos começa em 0. Duas chamadas a s.study() elevam seu valor primeiro para 5 e depois para 10. s.getName() continua retornando “Ada Lovelace”: o método herdado lê o campo privado declarado em Person.
A instrução super(name) passa o nome ao construtor de Person. Construtores não são herdados, portanto Student declara seu próprio construtor. Uma subclasse pode acrescentar métodos e sobrescrever comportamento herdado, mas não pode remover um método público herdado de sua API pública.
Identifique quais membros são acessíveis
| Modificador de acesso | Onde o membro pode ser acessado |
|---|---|
public | Qualquer classe com acesso ao tipo que declara o membro |
protected | O mesmo pacote, além do acesso por subclasses em outros pacotes |
| Sem modificador | O mesmo pacote |
private | A classe que declara o membro |
Membros privados não são herdados. O campo privado name ainda existe dentro de cada estudante, mas Student não pode lê-lo diretamente. Manter o campo privado e fornecer um método de acesso permite que Person controle seus próprios dados. Entre pacotes, o acesso a membros de instância protected tem uma restrição adicional: o código da subclasse acessa o membro por meio de si mesmo ou de uma referência do tipo da própria subclasse, não por meio de uma referência arbitrária da superclasse.
Teste a relação antes de estender
Java permite uma superclasse direta por classe. Uma cadeia como GraduateStudent extends Student e Student extends Person é válida; extends Student, Teacher não é. Uma classe sem superclasse explícita estende Object. A própria Object não tem superclasse.
Use o teste “é um”: todo objeto da subclasse pode ser usado onde se espera a superclasse? Um caminhão é um veículo. Um cliente tem um endereço, mas um cliente não é um endereço. Para essa segunda relação, use composição:
class Customer {
private Address address;
}
Aqui Address é outra classe. O campo permite que um cliente use um endereço sem afirmar que é um. Estender uma classe apenas para aproveitar um método conveniente também implica herdar todo o seu contrato público. Por fim, uma classe final não pode ser estendida. Como String é final, class ShortString extends String não compila.
Principais conclusões
- Um objeto de subclasse inclui o estado da superclasse e qualquer estado acrescentado pela subclasse.
- Os campos privados da superclasse são acessados por meio dos métodos da superclasse.
- Uma classe estende uma superclasse direta; construtores não são herdados.
- Use herança para uma relação “é um” e composição para uma relação “tem um”.
- Uma classe
finalnão pode ser estendida.
1.2 Passe valores por uma cadeia de construtores
Um construtor de subclasse deve organizar a construção da parte da superclasse do mesmo objeto. Ele pode chamar um construtor da superclasse com super(...) ou delegar a outro construtor da própria classe com this(...).
Escolha o construtor da superclasse
class Person {
private final String name;
Person(String name) { this.name = name; }
}
class Student extends Person {
private final String studentId;
Student(String name, String studentId) {
super(name);
this.studentId = studentId;
}
Student(String name) {
this(name, "no id yet");
}
}
O construtor com dois argumentos passa o nome a Person e armazena o identificador. O construtor com um argumento delega a ele. Um construtor contém no máximo uma invocação explícita de construtor: this(...) ou super(...). Seguir a cadeia de this(...) leva, por fim, a um construtor que invoca super(...).
Se nenhuma das duas for escrita, Java insere super(). Aqui isso falha porque Person tem apenas um construtor que exige um nome. Declarar qualquer construtor impede que Java forneça um construtor padrão sem argumentos. Uma classe sem construtor declarado recebe um construtor padrão que chama super().
Escrever new Person(name) dentro de Student faz outra coisa: constrói um objeto separado. Isso não inicializa o estado de pessoa do estudante atual nem substitui a chamada obrigatória ao construtor da superclasse.
Acompanhe a inicialização em ordem
class Base {
Base() { System.out.println("Base ready"); }
}
class Middle extends Base {
Middle() { System.out.println("Middle ready"); }
}
class Top extends Middle {
Top() { super(); System.out.println("Top ready"); }
}
new Top() entra na cadeia de construtores até Object. À medida que as chamadas retornam, a saída é “Base ready”, “Middle ready” e depois “Top ready”. Os inicializadores de campo de cada classe são executados após o retorno do construtor de sua superclasse e antes do restante do corpo do construtor dessa classe.
Um método que pode ser sobrescrito, quando chamado por um construtor da superclasse, já pode executar uma sobrescrita da subclasse. Se essa sobrescrita ler um campo de referência da subclasse cuja atribuição ainda não foi executada, ela encontrará null, mesmo que o campo tenha um inicializador na declaração. Limite o trabalho do construtor à inicialização do estado; evite chamadas a métodos que as subclasses possam sobrescrever.
Reconheça a mudança no Java 25
No Java 25, um construtor pode validar parâmetros antes de sua invocação explícita de construtor:
Student(String name, String studentId, int credits) {
if (credits < 0) {
throw new IllegalArgumentException("negative credits");
}
super(name);
this.studentId = studentId;
}
O Java 25 também permite atribuições simples antecipadas a campos declarados na classe atual sem inicializadores na declaração. Ele ainda proíbe ler os campos do objeto atual, chamar seus métodos de instância ou passar this antes da invocação. Para níveis padrão da linguagem anteriores ao Java 25, mantenha super(...) ou this(...) em primeiro lugar. O Java 22–24 ofereceu formas anteriores como recursos de prévia.
Principais conclusões
super(...)constrói a parte da superclasse do objeto atual.- Um
super()implícito precisa de um construtor sem argumentos acessível na superclasse. this(...)delega dentro da mesma classe; sua cadeia deve chegar a um construtor da superclasse.- Inicializadores de campo são executados após o retorno do construtor da superclasse.
- O Java 25 permite trabalho restrito antes de uma invocação explícita de construtor.
- Uma sobrescrita chamada durante a construção pode observar campos da subclasse antes de suas atribuições.
2Sobrescreva métodos e use a classe Object
Métodos herdados dão a objetos relacionados um ponto de partida comum. A sobrescrita permite que cada subclasse responda à mesma chamada à sua maneira, enquanto os métodos herdados de Object controlam como um objeto é exibido e comparado a outros. Pequenas diferenças de assinatura são importantes: um método pode parecer correto a quem o lê, mas não substituir o método que o código da biblioteca realmente chama. A igualdade também tem compromissos dos quais as coleções baseadas em hash dependem.
2.1 Substitua comportamento herdado com uma sobrescrita
Uma sobrescrita fornece a uma subclasse sua própria implementação de um método de instância herdado. Seu nome e seus tipos de parâmetros correspondem aos do método original, permitindo que quem o chama continue usando a mesma operação.
Amplie um método que já funciona
Suponha que a remuneração anual de um funcionário seja de doze pagamentos mensais. Um gerente também recebe um bônus anual:
class Employee {
private final double monthlySalary;
Employee(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
public double yearlyPay() { return 12 * monthlySalary; }
}
class Manager extends Employee {
private final double bonus;
Manager(double monthlySalary, double bonus) {
super(monthlySalary);
this.bonus = bonus;
}
@Override
public double yearlyPay() {
return super.yearlyPay() + bonus;
}
}
Para new Manager(3000.0, 5000.0), a chamada à superclasse retorna 36.000,0. Somar 5000,0 resulta em 41.000,0. super.yearlyPay() seleciona deliberadamente a implementação da superclasse. O método da superclasse continua disponível dentro da subclasse, embora chamadas comuns no objeto gerente executem a sobrescrita.
Escrever return yearlyPay() + bonus; chama novamente o próprio método do gerente. Nenhuma chamada termina, portanto a execução acaba falhando com StackOverflowError. A ausência de super. muda o significado da chamada.
Deixe o compilador verificar a assinatura
Coloque @Override acima de cada método que sobrescreve outro. Um erro de digitação como yearlypay() ou um parâmetro adicional causa então um erro de compilação. Sem a anotação, Java pode aceitar um novo método que quem chama o método nunca usa.
Uma sobrescrita válida segue estas regras:
- Seus tipos de parâmetros correspondem exatamente.
- Seu tipo de retorno é o mesmo ou um subtipo quando retorna um tipo de objeto. Esse estreitamento permitido é um tipo de retorno covariante.
- Seu acesso é pelo menos tão aberto quanto o do método herdado.
- Ela não acrescenta exceções verificadas que a declaração herdada proíbe.
Por exemplo, public String toString() não pode ser substituído por String toString() com acesso de pacote. Quem já chama o método deve manter o acesso.
Distinga sobrescrita, sobrecarga e ocultação
| Declaração | O que ela faz |
|---|---|
| Mesma assinatura de método de instância em uma subclasse | Sobrescreve comportamento herdado |
| Mesmo nome, lista de parâmetros diferente | Acrescenta uma sobrecarga |
| Mesma assinatura de método estático em uma subclasse | Oculta um método estático |
role() e role(String language) são sobrecargas; o segundo não substitui o primeiro. A escolha da sobrecarga usa os tipos declarados dos argumentos em tempo de compilação. Métodos estáticos também são selecionados pelo tipo usado em tempo de compilação, portanto não são sobrescritos dinamicamente.
Um método privado não é herdado; um método de mesmo nome na subclasse é independente. Um método final não pode ser sobrescrito, e construtores não podem ser sobrescritos. Além de compilar, o método ainda deve cumprir seu propósito original. Uma sobrescrita de gerente que retorne a remuneração mensal prejudicaria o código que totaliza a remuneração anual, mesmo que sua assinatura estivesse correta.
Principais conclusões
- Uma sobrescrita corresponde ao nome e aos tipos de parâmetros de um método de instância herdado.
@Overridedetecta sobrecargas acidentais e erros de digitação.super.method()reutiliza a implementação da superclasse.- Uma sobrescrita não pode reduzir o acesso nem ampliar as exceções verificadas permitidas.
- A sobrecarga altera os parâmetros; métodos estáticos são ocultados, e métodos final não podem ser sobrescritos.
2.2 Dê aos objetos texto útil e identifique sua classe
Toda classe Java estende Object direta ou indiretamente. Vetores também são objetos. Essa raiz compartilhada fornece métodos como toString(), equals(Object), hashCode() e getClass(), mesmo quando uma classe não declara nenhum deles.
Substitua a descrição exibida por padrão
O Object.toString() herdado produz um nome de classe, @ e o código de dispersão do objeto em hexadecimal. Um texto como “school.Person@1b6d3586” identifica a classe, mas não explica os dados da pessoa. O número pode mudar entre execuções e não é um endereço de memória garantido por Java.
Uma sobrescrita útil mostra os campos de que os leitores precisam. Aqui um estudante herdou getName() e armazena um campo credits:
@Override
public String toString() {
return getName() + " (" + credits + " credits)";
}
Para uma estudante chamada Ada Lovelace com 10 créditos, System.out.println(s) exibe “Ada Lovelace (10 credits)”. A chamada de impressão obtém automaticamente o texto do objeto. A concatenação de cadeias de caracteres também faz isso: "Student: " + s inclui a mesma descrição.
Para uma referência nula, a impressão e a concatenação produzem “null”. Uma chamada explícita s.toString() quando s é nulo lança NullPointerException, pois não há objeto para receber essa chamada.
Se Person.toString() já retorna o nome da pessoa, uma subclasse pode usá-lo como base:
@Override
public String toString() {
return super.toString() + ", student id " + studentId;
}
Para o identificador “S-17”, isso produz “Ada Lovelace, student id S-17”. A utilidade de super.toString() depende do que a superclasse realmente retorna. Se ela ainda usa a versão de Object, contribui com o texto padrão de classe e código de dispersão.
Mantenha separadas a identidade e as informações de classe
Object.equals(Object) compara a identidade: retorna verdadeiro quando ambas as referências se referem ao mesmo objeto. Dois objetos separados new Person("Ada") não são iguais segundo essa implementação herdada, apesar de terem nomes iguais. Classes como String, Integer e ArrayList fornecem sobrescritas baseadas em valor.
getClass() informa a classe real em tempo de execução:
Person p = new Student("Ada Lovelace");
System.out.println(p.getClass().getSimpleName());
Isso exibe “Student” quando Student tem o construtor mostrado. O tipo declarado da variável não altera a classe do objeto. getClass() é final e não pode ser sobrescrito.
hashCode() dá suporte a coleções baseadas em hash. Outros métodos de Object incluem o método protegido clone() e os métodos de coordenação de threads wait(), notify() e notifyAll().
Verifique a grafia exata
Java distingue toString() de tostring(). Um método toString(String prefix) é uma sobrecarga, portanto a impressão não o chama. Ambos os erros são detectados por @Override. Uma representação textual também não define igualdade: dois objetos podem exibir o mesmo texto e ainda assim ser considerados diferentes.
Principais conclusões
Objectfornece métodos comuns a todos os objetos Java, incluindo vetores.- A impressão e a concatenação de cadeias de caracteres usam
toString()e representam uma referência nula como “null”. Object.equalscompara identidade até que uma classe o sobrescreva.getClass()retorna a classe real e é final.
2.3 Mantenha a igualdade e o cálculo do código de dispersão consistentes
Dois objetos podem representar o mesmo valor sem ser o mesmo objeto. Um catálogo pode considerar dois objetos de livro iguais quando seus códigos de catálogo e suas edições correspondem. Defina essa regra sobrescrevendo equals(Object) e mantenha o cálculo do código de dispersão consistente com ela.
Compare os campos que definem o valor
import java.util.Objects;
final class Book {
private final String code;
private final int edition;
Book(String code, int edition) {
this.code = code;
this.edition = edition;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Book b)) return false;
return edition == b.edition && Objects.equals(code, b.code);
}
@Override
public int hashCode() {
return Objects.hash(code, edition);
}
}
A verificação de identidade aceita rapidamente o mesmo objeto. A verificação de tipo rejeita um objeto sem relação com a classe ou uma referência nula e então apresenta b como um Book. Edições primitivas usam ==; Objects.equals compara códigos com segurança mesmo quando um código é nulo. Dois livros com código “B-1042” e edição 2 são considerados iguais. A edição 3 representa um valor diferente.
O parâmetro deve ser Object. Escrever equals(Book other) acrescenta uma sobrecarga; isso mantém o equals(Object) herdado. Código da biblioteca que chama essa assinatura continuaria comparando a identidade. @Override faz a declaração incorreta falhar em tempo de compilação.
Siga os compromissos da igualdade
Para referências não nulas, a igualdade deve ser:
- Reflexiva: um objeto é igual a si mesmo.
- Simétrica: inverter os dois objetos produz a mesma resposta.
- Transitiva: se um objeto é igual a um segundo e o segundo é igual a um terceiro, o primeiro é igual ao terceiro.
- Consistente: comparações repetidas concordam enquanto os dados comparados permanecem inalterados.
Um objeto também deve ser considerado diferente de null. Objetos iguais devem retornar códigos de dispersão iguais. Objetos diferentes podem compartilhar um código de dispersão: uma colisão de hash não estabelece igualdade. Derive o código de dispersão dos mesmos campos usados na igualdade, como Objects.hash(code, edition) faz aqui.
Uma coleção baseada em hash usa o código de dispersão para localizar candidatos antes de verificar a igualdade. Sobrescrever apenas equals pode fazer um conjunto manter livros iguais separados ou fazer um mapa não encontrar uma chave igual. Alterar um campo usado no cálculo do código de dispersão enquanto seu objeto está armazenado como chave pode deixar esse objeto armazenado sob o código antigo.
Proteja a simetria entre subclasses
Suponha que uma Book extensível aceite qualquer instanceof Book, enquanto Ebook acrescente o tamanho do arquivo à igualdade e aceite apenas ebooks. Um livro pode aceitar um ebook com o mesmo código e a mesma edição, mas o ebook rejeita o livro. A comparação perdeu a simetria.
Uma abordagem exige classes correspondentes em tempo de execução, verificando se a referência é nula antes de getClass(). Outra torna a igualdade da superclasse final para que as subclasses não possam ampliar a regra de igualdade. Uma classe de valor final, como acima, evita completamente a igualdade entre subclasses. Records geram igualdade e códigos de dispersão a partir de seus componentes; constantes de enumeração já têm igualdade baseada em identidade, adequada a seus valores fixos.
Principais conclusões
- Sobrescreva
equals(Object), não uma sobrecarga com a classe como parâmetro. - A igualdade deve ser reflexiva, simétrica, transitiva e consistente; a comparação com null é falsa.
- Objetos iguais precisam de códigos de dispersão iguais; um código compartilhado não estabelece igualdade.
- Mantenha estáveis os campos de igualdade enquanto um objeto for chave de uma coleção baseada em hash ou elemento de um conjunto.
- Uma superclasse e uma subclasse não devem usar regras de igualdade que discordem quando os operandos são invertidos.
- Records e enums fornecem comportamento de igualdade integrado adequado.
3Defina contratos com classes abstratas e interfaces
Algumas classes relacionadas compartilham campos e métodos que já funcionam; outras classes compartilham apenas uma capacidade. Classes abstratas e interfaces expressam essas relações diferentes. Um programa de desenho pode manter dados comuns de formas em uma classe abstrata, enquanto um programa de carregamento trabalha com dispositivos sem relação entre si por meio de uma interface. Ambas as abordagens permitem que o compilador verifique o comportamento exigido antes da execução do programa, e Java fornece regras precisas quando implementações herdadas competem.
3.1 Compartilhe estado e exija comportamento das subclasses
Uma classe abstrata é uma classe base que não pode ser instanciada diretamente. Ela pode conter campos comuns e métodos que já funcionam, deixando operações selecionadas para as subclasses completarem.
Deixe uma operação obrigatória abstrata
abstract class Shape {
private final String name;
Shape(String name) { this.name = name; }
public abstract double area();
public String describe() {
return name + " with area " + area();
}
}
class Rectangle extends Shape {
private final double width;
private final double height;
Rectangle(double width, double height) {
super("rectangle");
this.width = width;
this.height = height;
}
@Override
public double area() { return width * height; }
}
area() tem uma assinatura e um ponto e vírgula, sem corpo. Ele estabelece o que toda forma concreta deve fornecer. Como Shape contém um método abstrato, a classe também deve ser abstrata. Rectangle fornece a implementação que falta e pode ser instanciada.
Para new Rectangle(3.0, 4.0), o construtor da superclasse armazena “rectangle”. O construtor da subclasse armazena as dimensões. Sua área é de 12,0 metros quadrados quando as dimensões estão em metros. Chamar describe() retorna “rectangle with area 12.0”. A chamada a area() no método herdado executa a implementação do retângulo.
Use construtores e métodos concretos normalmente
Uma classe abstrata pode ter campos, construtores e métodos com corpo, com os níveis de acesso usuais. Seu construtor é executado como parte da criação de um objeto de subclasse. new Shape() é proibido mesmo que exista um construtor correspondente; new Rectangle(3.0, 4.0) é válido.
Uma segunda subclasse pode usar a mesma descrição herdada enquanto calcula a área de outra forma. Uma implementação de círculo retorna Math.PI * radius * radius. Para um raio de 1,5 m, o cálculo é π vezes 2,25, aproximadamente 7,07 metros quadrados. O método compartilhado describe() não precisa de um ramo específico para círculos.
Uma variável ainda pode ter o tipo abstrato: Shape s = new Rectangle(3.0, 4.0);. Java proíbe construir a classe abstrata, não referenciar um objeto concreto por meio dela.
Faça o trabalho ausente causar um erro de compilação
Toda subclasse concreta deve fornecer ou herdar implementações de todos os métodos abstratos. Uma subclasse que deixa alguns incompletos também deve ser declarada abstrata. Retornar 0 de um area() provisório permitiria que uma subclasse concreta incompleta compilasse e produzisse silenciosamente resultados errados. Declarar area() abstrato torna esse trabalho ausente visível ao compilador.
Métodos abstratos não podem ser privados, estáticos ou final: cada um desses modificadores impediria a sobrescrita da subclasse que fornece a operação. Uma classe abstrata pode não conter métodos abstratos; nesse caso, o modificador apenas impede a construção direta. Use esse tipo de classe base quando suas subclasses passarem no teste “é um” e se beneficiarem de estado ou código compartilhado.
Principais conclusões
- Uma classe abstrata não pode ser construída diretamente, mas seu construtor é executado para objetos de subclasses.
- Um método abstrato não tem corpo e exige uma implementação concreta.
- Uma subclasse concreta deve completar todo método abstrato herdado.
- Um tipo abstrato pode ser usado para variáveis, parâmetros e listas.
- Classes abstratas podem combinar campos comuns, métodos que funcionam e operações obrigatórias.
3.2 Dê um contrato compartilhado a classes sem relação entre si
Uma interface nomeia uma capacidade que várias classes podem fornecer. Ela estabelece os métodos disponíveis por meio dessa capacidade sem exigir que as classes compartilhem os mesmos campos de instância ou a mesma superclasse.
Declare e implemente os métodos
interface Chargeable {
int batteryPercent();
void charge(int minutes);
}
class Phone implements Chargeable {
private int battery;
Phone(int battery) { this.battery = battery; }
@Override
public int batteryPercent() { return battery; }
@Override
public void charge(int minutes) {
battery = Math.min(100, battery + 2 * minutes);
}
}
Os métodos abstratos da interface são implicitamente públicos. Os métodos que os implementam devem ser explicitamente públicos; omitir esse modificador reduziria o acesso e causaria uma falha de compilação. Uma classe concreta deve fornecer ou herdar implementações públicas adequadas de todos os métodos abstratos. Uma classe abstrata que implementa a interface pode deixar alguns para suas subclasses.
Para um telefone com 35 por cento de carga, charge(20) acrescenta 40 pontos percentuais e deixa a carga em 75 por cento. Mais 20 minutos levariam a 115 antes da limitação, portanto Math.min armazena 100. Uma ElectricScooter pode implementar a mesma interface acrescentando apenas um ponto por minuto: começando com 50 por cento, ela chega a 70 após 20 minutos. A interface define a operação disponível, enquanto a classe fornece seu comportamento.
Use a interface como tipo
Chargeable device = new Phone(35);
device.charge(20);
System.out.println(device.batteryPercent());
Isso exibe 75. Quem faz a chamada pode usar os métodos da interface e os métodos públicos disponíveis em todos os objetos. Operações exclusivas de Phone não estão disponíveis por meio de device, a menos que a interface declarada as exponha.
Interfaces também podem ser tipos de parâmetros, de retorno e de elementos de listas. Um método que aceita Chargeable pode trabalhar com um telefone ou uma scooter sem conhecer a organização de seus campos. A biblioteca Java usa a mesma abordagem: List tem implementações como ArrayList e LinkedList; Runnable declara run(); Comparable declara uma comparação natural.
Combine mais de uma capacidade
Uma classe estende uma classe e pode implementar várias interfaces. A ordem é class Phone extends Device implements Chargeable, Connectable. Uma interface pode estender várias interfaces: interface SmartDevice extends Chargeable, Connectable. As classes que a implementam devem então fornecer todas as operações abstratas herdadas.
Uma interface não tem construtores nem campos de instância. new Chargeable() não compila. Um campo declarado como int FULL = 100; é implicitamente uma constante public static final, lida como Chargeable.FULL.
Um método abstrato de interface não fornece código funcional. Sua implementação pode vir da própria classe ou de um método público herdado da superclasse. Métodos padrão são os métodos de interface que podem fornecer comportamento de instância herdado.
Principais conclusões
- Uma interface é um tipo que descreve uma capacidade compartilhada.
- Uma classe concreta que implementa uma interface deve fornecer ou herdar implementações públicas adequadas.
- Uma classe pode implementar várias interfaces enquanto estende uma classe.
- Interfaces não têm construtores nem campos de instância; seus campos são constantes.
- O tipo de interface permite que quem faz a chamada use seu contrato com classes sem relação entre si.
3.3 Use comportamento compartilhado de interfaces e resolva conflitos
Interfaces podem fornecer código funcional além de declarações de métodos abstratos. O modificador do método determina se os objetos das classes que implementam a interface o herdam, se quem o chama usa o nome da interface ou se apenas código dentro da interface pode usá-lo.
Construa comportamento padrão a partir do contrato
interface Chargeable {
int batteryPercent();
void charge(int minutes);
default boolean needsCharge() {
return batteryPercent() < 20;
}
static int clamp(int percent) {
return Math.max(0, Math.min(100, percent));
}
}
Um método padrão é um método público de instância com corpo. Um telefone que informa 15 por cento herda needsCharge() e retorna verdadeiro. Com 20 por cento, retorna falso. O método padrão chama a operação abstrata batteryPercent(); ele não lê um campo privado do telefone. Uma classe pode sobrescrever o método com sua própria implementação pública.
Um método estático de interface pertence à interface: Chargeable.clamp(130) retorna 100 e Chargeable.clamp(-5) retorna 0. As classes que implementam a interface não o herdam, portanto Phone.clamp(130) não é uma chamada válida a esse método. Métodos padrão e estáticos de interface foram introduzidos no Java 8.
Desde o Java 9, uma interface também pode declarar métodos privados para compartilhar código auxiliar entre seus próprios métodos. Um método auxiliar privado de instância dá suporte a métodos padrão; um método auxiliar privado estático também pode dar suporte a métodos estáticos. As classes que implementam a interface não podem chamar esses métodos auxiliares diretamente.
Resolva conflitos entre métodos padrão
Duas interfaces sem relação entre si podem fornecer a mesma assinatura:
interface Lamp {
default String describe() { return "lamp"; }
}
interface Speaker {
default String describe() { return "speaker"; }
}
class SmartLamp implements Lamp, Speaker {
@Override
public String describe() {
return Lamp.super.describe() + " and "
+ Speaker.super.describe();
}
}
Sem a sobrescrita, SmartLamp tem um conflito e não compila. As chamadas qualificadas InterfaceName.super.method() selecionam os métodos padrão individuais de suas superinterfaces diretas. Essa implementação retorna “lamp and speaker”.
| Implementações em conflito | Regra |
|---|---|
| Método público concreto herdado da superclasse e método padrão de interface | O método da classe prevalece |
| Método padrão de uma interface e de outra que a estende | A interface mais específica prevalece |
| Métodos padrão de interfaces sem relação entre si | A classe deve resolver o conflito |
Uma declaração abstrata na superclasse também pode exigir que uma subclasse concreta implemente o método; um método padrão de interface não elimina essa obrigação.
Considere as classes que já implementam a interface
Acrescentar um método abstrato exige que cada classe concreta que implementa a interface forneça ou herde uma implementação correspondente. Um método padrão muitas vezes evita essa alteração ao fornecer comportamento funcional, mas pode introduzir um conflito com outro método herdado. Métodos padrão ajudam uma interface a evoluir; as regras de herança ainda determinam se as classes resultantes compilam.
Principais conclusões
- Um método padrão fornece comportamento público de instância que pode ser sobrescrito.
- Chame um método estático de interface por meio do nome da interface.
- Métodos privados de interface são auxiliares visíveis apenas dentro da interface.
- Um método concreto de classe tem prioridade sobre um método padrão de interface.
- Métodos padrão em conflito de interfaces sem relação entre si exigem uma sobrescrita; caso contrário, prevalece o método padrão da interface mais específica.
- Acrescentar um método padrão pode introduzir conflitos entre métodos herdados.
3.4 Escolha uma base compartilhada, uma capacidade ou ambas
Uma classe abstrata e uma interface podem definir métodos obrigatórios. A escolha depende da relação entre os objetos e de a implementação precisar ou não de estado de instância compartilhado.
Compare o que cada uma oferece
| Pergunta | Classe abstrata | Interface |
|---|---|---|
| Pode armazenar campos por objeto? | Sim | Não |
| Pode ter construtores? | Sim | Não |
| Pode fornecer métodos de instância funcionais? | Sim | Sim, com métodos padrão |
| Quantas uma classe pode usar diretamente? | Uma superclasse | Várias interfaces implementadas |
| Quais níveis de acesso estão disponíveis? | Todos os níveis usuais | Métodos públicos e auxiliares privados |
Campos de interface são constantes public static final, não dados de cada objeto. Métodos auxiliares privados de interface dão suporte aos próprios métodos funcionais da interface; eles não criam estado privado para cada objeto que a implementa.
Coloque estado estreitamente relacionado em uma classe abstrata
Um programa de desenho tem círculos, retângulos e triângulos. Cada forma armazena um nome e uma posição, e cada uma calcula sua área de modo diferente. Uma Shape abstrata pode manter os campos comuns e o construtor, fornecer métodos funcionais de movimento ou descrição e declarar area() como abstrato.
Todas as formas concretas são tipos de Shape, portanto outro código pode pedir uma forma e receber qualquer uma delas. Da mesma forma, uma Enemy abstrata pode manter pontos de vida e um método compartilhado takeDamage(int points), deixando o movimento para classes específicas de inimigos. O estado compartilhado dá a essas classes base uma tarefa útil.
Coloque uma capacidade transversal em uma interface
O mesmo programa de desenho pode exportar formas, rótulos de texto e imagens. Exportar não torna um rótulo de texto uma forma, e esses objetos já podem precisar de superclasses diferentes. Uma interface Exportable pode declarar String exportText(). Cada classe implementa a operação usando seus próprios dados.
Uma classe pode combinar as duas funções: class Circle extends Shape implements Exportable. A superclasse fornece o estado da forma e código compartilhado; a interface permite que o círculo seja usado pelo código de exportação. Uma classe que implementa várias interfaces se compromete a oferecer várias capacidades sem herdar delas campos de instância.
Combine um contrato com uma implementação parcial
Uma classe abstrata pode implementar uma interface e completar apenas alguns métodos. As subclasses concretas completam as operações restantes. List e AbstractList do Java usam essa organização; ArrayList estende AbstractList. Quem faz as chamadas pode depender da interface enquanto quem implementa reutiliza uma classe base quando ela é adequada.
Por exemplo, List<String> names = new ArrayList<>(); permite que quem faz as chamadas acrescente, leia e remova nomes por meio de List. Substituir o inicializador por new LinkedList<>() preserva essas chamadas. Um parâmetro declarado como ArrayList<String> rejeitaria um LinkedList<String>, mesmo que precisasse apenas de operações de lista.
Uma classe abstrata que contém apenas métodos abstratos ainda ocupa a única posição de superclasse da subclasse. Ela não é intercambiável com uma interface. Escolha a interface quando apenas o contrato comum for necessário e acrescente uma classe base quando implementações relacionadas se beneficiarem de estado ou código compartilhado.
Principais conclusões
- Use uma classe abstrata para tipos relacionados de objetos que compartilham estado ou código.
- Use uma interface para uma capacidade que classes sem relação entre si possam fornecer.
- Uma classe pode estender uma base abstrata e implementar interfaces ao mesmo tempo.
- Uma implementação abstrata parcial pode dar suporte a uma interface sem restringir quem faz as chamadas a essa classe base.
4Escreva programas flexíveis com polimorfismo
O tipo de uma variável e a classe de um objeto respondem a perguntas diferentes. O tipo declarado da variável controla quais chamadas compilam; a classe real do objeto controla qual sobrescrita é executada. Manter essas decisões separadas permite acompanhar código desconhecido, usar conversões explícitas com segurança e escrever laços que funcionam com novas implementações. A mesma abordagem dá suporte a interfaces da biblioteca, como Comparable, que fornece aos objetos uma ordem natural de classificação.
4.1 Distinga o tipo da referência da classe do objeto
O polimorfismo permite que uma referência geral aponte para objetos de diferentes subtipos. O objeto mantém todo o seu estado e comportamento; a referência oferece a quem faz chamadas uma visão específica das operações disponíveis.
Identifique os dois tipos
Person p = new Student("Ada Lovelace", "S-17");
O tipo declarado, também chamado de tipo estático, é Person: ele aparece na declaração da variável. O tipo real, ou tipo em tempo de execução, é Student: essa é a classe instanciada por new. A classe real do objeto não muda após a construção.
Atribuir uma referência de estudante a uma referência de pessoa é uma conversão para supertipo. Ela é automática e segura porque todo estudante é uma pessoa. O mesmo estudante também pode ser referenciado por uma variável Object. Um objeto que implementa uma interface pode ser convertido para o tipo dessa interface, como em Chargeable c = new Phone(35);.
Nenhum dado do estudante é apagado pela atribuição. Continua existindo um único objeto estudante com seu identificador de estudante, dados de pessoa herdados e métodos sobrescritos. Atribuir a ele uma referência mais geral muda o que o compilador permite acessar por meio dessa referência.
Verifique as chamadas pelo tipo declarado
Se Person declara getName(), então p.getName() compila. Se apenas Student declara getStudentId(), então p.getStudentId() não compila. O compilador verifica a API de Person mesmo quando o inicializador claramente cria um estudante.
A atribuição inversa Student s = p; não é aceita sem uma conversão explícita de tipo. Uma referência de pessoa pode apontar para uma pessoa que não é estudante. Essa restrição protege o código de tratar um objeto como um subtipo ao qual ele não pertence.
| Tipo declarado | Chamadas disponíveis por meio da referência |
|---|---|
Object | Operações públicas fornecidas por Object |
Person | Operações acessíveis de Person, incluindo as herdadas |
Student | Operações acessíveis de estudante e operações herdadas |
Permita que parâmetros e listas aceitem vários subtipos
Um método static void printCard(Person person) pode receber um objeto Person, Student ou Teacher, supondo que os dois últimos estendam Person. Seu código pode depender do contrato de pessoa. Um tipo de retorno também pode ser geral, por exemplo, um método que retorna um Shape.
Uma lista com um tipo geral de elemento pode armazenar diferentes classes de formas:
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(1.5));
shapes.add(new Rectangle(3.0, 4.0));
for (Shape shape : shapes) {
System.out.println(shape.area());
}
Aqui List e ArrayList vêm de java.util, e cada forma concreta fornece area(). O laço usa apenas essa operação comum.
Uma List<Circle> não é uma List<Shape>. Se fosse, um código que a recebesse como lista de formas poderia acrescentar um retângulo a uma lista que deveria conter apenas círculos. Quando um método exige List<Shape>, crie a lista com esse tipo de elemento desde o início.
Principais conclusões
- O tipo declarado controla quais operações uma referência pode acessar.
- O tipo real é a classe do objeto e não muda após a construção.
- A conversão para supertipo, para uma superclasse ou interface implementada, é automática e segura.
- Um tipo geral de parâmetro ou de elemento de lista pode aceitar vários subtipos concretos.
List<Circle>não pode ser usada comoList<Shape>.
4.2 Acompanhe a seleção de sobrecarga e a vinculação dinâmica
Uma chamada de método é resolvida em duas etapas. Em tempo de compilação, Java verifica os tipos declarados e escolhe uma assinatura de método. Em tempo de execução, a vinculação dinâmica escolhe a implementação sobrescrita do método de instância para a classe real do objeto.
Acompanhe uma chamada dentro de um método herdado
class A {
String who() { return "A"; }
String hello() { return "Hello from " + who(); }
}
class B extends A {
@Override String who() { return "B"; }
}
class C extends B { }
Para A x = new C();, x.hello() é permitido porque A declara hello(). A classe C não tem uma sobrescrita de hello(), nem B, portanto Java executa a implementação em A.
Dentro desse método, who() significa this.who(). O objeto receptor continua sendo o objeto C. Java procura a partir de C e encontra a implementação de who() mais próxima em B. O resultado é “Hello from B”. Entrar em um método herdado não transforma o objeto receptor em um objeto da superclasse.
Escolha uma sobrecarga antes de escolher uma sobrescrita
static String kind(Object value) { return "object"; }
static String kind(String value) { return "string"; }
Dentro de uma classe que contém esses métodos, Object value = "hi"; kind(value) seleciona kind(Object) e retorna “object”. O tipo declarado da variável é Object; Java não reconsidera as sobrecargas usando a cadeia de caracteres real em tempo de execução.
Para uma chamada de instância, o despacho em tempo de execução ainda pode escolher uma sobrescrita da assinatura já selecionada. Ele não pode mudar para outra sobrecarga que apenas pareça mais específica para o argumento real. Primeiro identifique a assinatura, depois procure sua sobrescrita.
Reconheça chamadas e membros que não usam esse despacho
| Membro ou chamada | O que o seleciona |
|---|---|
| Método de instância sobrescrito | Classe real do objeto receptor, após a seleção da assinatura |
| Método estático | Tipo usado para a chamada em tempo de compilação |
| Campo ocultado | Tipo declarado da referência |
| Método privado | Código da classe que o declara |
super.method() | Implementação da superclasse |
Se P declara String label = "P"; e Q extends P declara seu próprio String label = "Q";, então P p = new Q(); p.label lê “P”. Campos são ocultados, não sobrescritos. Ambos os campos existem no objeto. Evite reutilizar nomes de campos herdados, pois o tipo da referência pode fazer acessos aparentemente idênticos lerem estados diferentes.
super.method() seleciona a implementação da superclasse, incluindo uma que a superclasse herde. Se o próprio método selecionado fizer uma chamada comum em this, essa chamada interna ainda poderá ser despachada para uma sobrescrita da subclasse.
Principais conclusões
- O tempo de compilação determina a assinatura que pode ser chamada, incluindo a escolha da sobrecarga.
- A vinculação dinâmica seleciona a sobrescrita mais próxima dessa assinatura, buscando a partir da classe real para cima.
- Uma chamada comum dentro de um método herdado ainda usa o objeto receptor real.
- Campos, métodos estáticos, métodos privados e chamadas explícitas a
supernão usam o despacho comum de sobrescritas.
4.3 Verifique um subtipo antes de usar seus métodos específicos
Uma referência geral às vezes precisa acessar uma operação que pertence apenas a um subtipo. Uma conversão para subtipo informa ao compilador que deve tratar a referência como esse subtipo. Em tempo de execução, Java verifica se o objeto já tem esse tipo.
Entenda o que uma conversão explícita muda
Suponha que Person p se refira a um estudante. Student s = (Student) p; permite s.getStudentId(). Ambas as variáveis continuam se referindo ao mesmo objeto. A conversão não o copia, não altera sua classe real nem acrescenta campos de estudante a uma pessoa comum.
Se p de fato se referir a new Person("Grace Hopper"), essa conversão lança ClassCastException. O compilador aceita a possível relação de pessoa para estudante, mas o objeto em tempo de execução ainda deve satisfazê-la. Uma conversão explícita entre classes que não podem ter relação, como uma referência Person convertida para a classe final Integer, falha em tempo de compilação.
Isso difere de uma conversão de tipo primitivo: (int) 3.9 cria o valor inteiro 3. Uma conversão explícita de referência apenas verifica e altera o tipo pelo qual uma referência existente é usada.
Combine o teste e a nova variável
Desde o Java 16, a correspondência de padrões torna o caso seguro mais conciso:
static void showStudentId(Person person) {
if (person instanceof Student student) {
System.out.println(student.getStudentId());
}
}
O teste é verdadeiro para um estudante ou um objeto de uma subclasse de estudante. A variável student está disponível dentro do bloco, onde se sabe que o teste teve sucesso. Uma pessoa comum pula o bloco. Uma referência nula também torna o teste falso, portanto o bloco nunca tenta chamar um método em null.
A forma antiga testa person instanceof Student e depois declara Student student = (Student) person; dentro do ramo em que o teste teve sucesso. Ambas as formas protegem a conversão. Testar um objeto sem relação em outro ponto não protege uma conversão de person; o teste e a conversão devem envolver a mesma referência.
Uma conversão para supertipo não precisa de conversão explícita. Person person = student; já compila e não pode falhar por incompatibilidade de tipos. Uma conversão explícita de null para um tipo de referência compatível produz null; usar esse resultado para chamar um método de instância ainda falha com NullPointerException.
Decida se um teste de tipo é necessário
Testes de tipo são adequados para código que recebe um Object, como equals(Object other), ou precisa de uma operação exclusiva de um subtipo, como um identificador de estudante. Eles também podem ajudar ao trabalhar com classes que não podem ser alteradas.
Uma cadeia repetida de testes de tipo para uma operação compartilhada é um problema diferente. Se o código verifica cada classe de forma para calcular sua área, cada forma acrescentada exige outro ramo. Declarar area() em Shape e deixar que as subclasses o implementem permite que quem faz a chamada use shape.area().
Uma conversão explícita não pode tornar uma operação comum ausente disponível em todos os subtipos. Coloque o comportamento compartilhado no contrato comum e reserve um teste de subtipo para o trabalho que realmente exige esse subtipo específico.
Principais conclusões
- Uma conversão para subtipo verifica um objeto existente; ela não transforma o objeto.
- Uma conversão possível pode compilar e ainda lançar
ClassCastExceptionem tempo de execução. instanceofé falso para null e verdadeiro para instâncias do tipo ou de seus subtipos.- Um padrão
instanceofintroduz uma variável com tipo seguro onde se sabe que o teste é verdadeiro. - Operações compartilhadas pertencem ao tipo comum; operações exclusivas de um subtipo podem precisar de um teste de tipo.
4.4 Escreva laços gerais e defina uma ordem natural
Um método continua útil com novas classes quando seu parâmetro expõe exatamente as operações de que ele precisa. Quem faz a chamada fornece os objetos; a vinculação dinâmica fornece seu comportamento individual.
Some áreas sem ramos específicos por classe
static double totalArea(List<Shape> shapes) {
double total = 0;
for (Shape shape : shapes) {
total += shape.area();
}
return total;
}
Com um círculo de raio 1,5 m, um retângulo de dimensões 3,0 m e 4,0 m e um triângulo de base 4,0 m e altura 2,5 m, as áreas são aproximadamente 7,06858, 12,0 e 5,0 metros quadrados. Somar a área não arredondada do círculo produz aproximadamente 24,07 metros quadrados. Cada objeto fornece seu próprio area().
Acrescentar uma classe Triangle não exige editar o laço. A nova classe implementa a operação de forma existente. Um resultado negativo violaria o contrato da área mesmo que a sobrescrita compilasse; o código geral depende tanto do significado da operação quanto de sua assinatura.
A mesma abordagem funciona com uma interface. Um método chargeAll(List<Chargeable> devices, int minutes) percorre os dispositivos e chama device.charge(minutes). Após 20 minutos, o telefone que começa com 35 por cento chega a 75 por cento e a scooter que começa com 50 por cento chega a 70 por cento. O laço nunca seleciona uma regra de carregamento por conta própria.
Deixe quem chama escolher uma implementação de lista
Um parâmetro List<Shape> aceita tanto um ArrayList<Shape> quanto um LinkedList<Shape>. Um parâmetro ArrayList<Shape> rejeita a lista encadeada, apesar de precisar apenas de operações de lista. Use o tipo mais geral que forneça os métodos necessários; declarar tudo como Object seria geral demais, pois ele não tem area() nem operações de lista.
Torne objetos ordenáveis com Comparable
Comparable<T> define a ordem natural de um objeto. O resultado de seu compareTo é negativo quando este objeto vem antes do outro, zero para um empate na ordenação e positivo quando vem depois. O sinal do resultado é o que importa; ele não precisa ser exatamente −1 ou 1.
record Runner(String name, int seconds)
implements Comparable<Runner> {
@Override
public int compareTo(Runner other) {
return Integer.compare(seconds, other.seconds);
}
}
Uma lista modificável com Mia em 3605 s, Ken em 3490 s e Ana em 3720 s passa a ser Ken, Mia, Ana após Collections.sort(runners). list.sort(null) também usa a ordem natural. Construa uma lista modificável com new ArrayList<>(List.of(...)); tentar ordenar o resultado não modificável de List.of(...) lança UnsupportedOperationException.
Use Integer.compare em vez de subtrair os valores. A subtração pode sofrer estouro para valores grandes com sinais opostos e retornar o sinal errado. A mesma ideia se aplica a Double.compare para campos decimais. Neste exemplo de corredores, tempos iguais empatam na ordenação mesmo que os nomes sejam diferentes, portanto um empate na comparação e a igualdade de objetos não precisam significar a mesma coisa. Prefira uma ordem natural cujo resultado zero concorde com a igualdade; documente uma diferença intencional.
Principais conclusões
- Código geral chama uma operação compartilhada e permite que cada objeto forneça sua implementação.
- Um parâmetro de interface pode aceitar diferentes implementações do mesmo contrato.
compareTousa um resultado negativo, zero ou positivo para expressar a ordenação.Integer.compareevita o risco de estouro causado pela subtração de campos inteiros.- A ordenação exige uma lista modificável;
List.of(...)sozinho produz uma lista não modificável. - Um empate na ordenação pode diferir da igualdade por valor quando a comparação usa menos campos.
5Modele valores fixos e dados com enums e records
Alguns programas precisam de um conjunto fixo de opções, enquanto outros precisam de um pequeno grupo de valores que pertencem uns aos outros. Enums dão às opções um tipo próprio; records fornecem grande parte do código rotineiro para objetos de dados. Ambos se encaixam no sistema de tipos do Java e podem implementar interfaces. Seu comportamento integrado poupa trabalho, mas a escolha entre eles e uma classe comum ainda depende do que pode mudar e do que torna dois valores iguais.
5.1 Represente opções fixas com um enum
Um enum dá a um conjunto fixo de opções nomeadas um tipo próprio. enum Size { SMALL, MEDIUM, LARGE } permite Size size = Size.MEDIUM;. Ele impede que quem chama um método que exige Size passe a cadeia de caracteres escrita incorretamente “MEDUIM” ou o inteiro 7.
Use nomes, constantes e switch
Cada constante de enumeração é um único objeto, portanto size == Size.LARGE é uma comparação adequada. Uma variável de enum também pode ser nula. Size.values() retorna todas as constantes na ordem de declaração; Size.valueOf("LARGE") encontra o nome exato. A busca distingue maiúsculas de minúsculas: “large” causa IllegalArgumentException.
name() retorna o nome declarado. ordinal() retorna a posição a partir de zero, portanto Size.MEDIUM.ordinal() é 1. Inserir uma constante altera as posições posteriores, portanto uma posição ordinal é uma má escolha como código armazenado ou valor de negócio. Por padrão, toString() também retorna o nome.
Uma expressão switch, padrão desde o Java 14, pode retornar um valor:
String label = switch (size) {
case SMALL -> "S";
case MEDIUM -> "M";
case LARGE -> "L";
};
Nomear todas as constantes torna a expressão exaustiva sem um ramo default. Esses casos pressupõem um size não nulo; listar as constantes não trata null.
Dê dados significativos a cada constante
enum CupSize {
SMALL(250), MEDIUM(350), LARGE(500);
private final int milliliters;
CupSize(int milliliters) { this.milliliters = milliliters; }
public int milliliters() { return milliliters; }
}
O ponto e vírgula separa a lista de constantes dos membros. O construtor é executado uma vez para cada constante quando o enum é inicializado. Ele é implicitamente privado, e quem usa o enum não pode criar novas constantes com new CupSize(400).
Dois copos grandes e um pequeno comportam 1250 mL, ou 1,25 L. Esse cálculo usa o campo milliliters, não as posições das constantes. Percorrer CupSize.values() e exibir cada constante e sua capacidade produz “SMALL: 250 mL”, “MEDIUM: 350 mL” e “LARGE: 500 mL”.
Dê às constantes seu próprio comportamento
Um enum pode implementar uma interface e pode dar às constantes individuais corpos de métodos diferentes:
enum Operation {
ADD { int apply(int a, int b) { return a + b; } },
MULTIPLY { int apply(int a, int b) { return a * b; } };
abstract int apply(int a, int b);
}
Operation.MULTIPLY.apply(6, 7) retorna 42. Cada constante completa a operação abstrata. Enums estendem implicitamente java.lang.Enum; eles não podem estender outra classe, e classes comuns não podem estendê-los.
Use um enum para opções fixas quando o programa é escrito, como direções ou estados de pedidos. A lista de produtos de uma loja, que muda com o tempo, pertence aos dados: acrescentar produtos não deve exigir reescrever e recompilar uma declaração de enum.
Principais conclusões
- Um enum dá a um conjunto fixo de constantes um tipo distinto.
values()preserva a ordem de declaração evalueOfcorresponde ao nome exato.- Armazene números significativos em campos, pois as posições ordinais podem mudar.
- Construtores de enum inicializam constantes; quem usa o enum não pode usar
newpara acrescentar constantes. - Enums podem implementar interfaces e dar comportamentos diferentes às constantes.
5.2 Transporte valores com um record e valide-os
Um record é uma classe de dados concisa, padrão desde o Java 16. Seu cabeçalho lista os valores, chamados componentes, que definem cada instância: record Point(int x, int y) { }.
Use os membros gerados
O compilador fornece um campo privado final para cada componente, um construtor canônico com esses componentes em ordem e métodos de acesso públicos com os nomes deles. Ele também fornece equals, hashCode e toString baseados nos componentes.
Point a = new Point(3, 4);
Point b = new Point(3, 4);
System.out.println(a.x());
System.out.println(a.equals(b));
System.out.println(a == b);
Isso exibe 3, true e false em linhas separadas. Valores de componentes correspondentes tornam os records iguais, mas as duas expressões new criam objetos separados. Records iguais têm códigos de dispersão iguais. Seu texto gerado inclui os nomes do record e dos componentes; JDKs atuais mostram “Point[x=3, y=4]”. Esse formato exato de texto não é garantido, portanto não o analise como formato de dados.
Os métodos de acesso são x() e y(), não getX() e getY(). Um método modificador que atribui this.x = x não compila porque o campo do componente é final. Um método pode retornar um novo valor: Point moveRight(int dx) { return new Point(x + dx, y); }.
Valide em um construtor compacto
record Temperature(double celsius) {
Temperature {
if (celsius < -273.15) {
throw new IllegalArgumentException("below absolute zero");
}
}
}
Um construtor compacto repete o nome do record sem uma lista de parâmetros. Ele verifica ou normaliza os parâmetros recebidos; Java atribui valores aos campos dos componentes após seu corpo. Essa verificação rejeita −300 °C e aceita 21,5 °C. Um componente de texto poderia ser normalizado com name = name.trim(); antes da atribuição automática.
Um record público precisa de um construtor compacto público. Records também podem declarar campos estáticos, métodos estáticos e métodos de instância. Um construtor adicional delega ao construtor canônico com this(...). Campos de instância adicionais além dos componentes são proibidos.
Proteja objetos mutáveis dos componentes
Campos de componentes final tornam a imutabilidade superficial. Um record que contém uma lista mutável continua se referindo a essa lista:
record Team(String name, List<String> members) {
Team {
members = List.copyOf(members);
}
}
Com java.util.List importado, isso armazena uma cópia não modificável. Acréscimos posteriores à lista original não afetam a composição da equipe, e team.members().add("Lee") lança UnsupportedOperationException. A cópia protege a estrutura da lista; ela não torna profundamente imutáveis os objetos mutáveis dos elementos.
Records estendem implicitamente java.lang.Record e são final. Eles não podem estender outra classe nem ser estendidos, mas podem implementar interfaces como Comparable. Use-os para dados definidos por seus valores; use uma classe comum para estado variável, campos de instância ocultos adicionais ou uma hierarquia de classes extensível.
Principais conclusões
- O cabeçalho de um record define os componentes e o construtor, os métodos de acesso e os métodos de valor gerados.
- Métodos de acesso de record usam nomes dos componentes, como
x(). - Um construtor compacto verifica ou normaliza parâmetros antes da atribuição automática aos campos.
- As referências dos componentes de um record são final, mas os objetos referenciados podem continuar mutáveis.
- Uma cópia não modificável da lista protege a estrutura de lista de um record.
- Um record é final, não pode estender uma classe explicitamente e pode implementar interfaces.
Exercícios resolvidos de Herança, interfaces e polimorfismo em Java
1Construa um dispositivo com herança passo a passo
12 passos
- 1IntroduçãoUm objeto com uma função geral e outra específica
Um sensor é um tipo de dispositivo. Todo dispositivo precisa de um rótulo, enquanto um sensor também precisa de uma unidade de medida. A herança permite que o dispositivo seja responsável por seu rótulo e o sensor acrescente sua unidade.
Vamos construir essa relação, usar um método herdado e acompanhar como as chamadas de construtores inicializam cada parte. A pergunta principal é a qual classe pertence cada campo e, portanto, qual construtor deve atribuir seu valor.
- 2ExemploComece com o estado de que todo dispositivo precisa
class Device { private final String label; Device(String label) { this.label = label; } public String label() { return label; } }Devicecontrola seu rótulo privado. Outros códigos o leem por meio delabel(). Uma subclasse herdará esse método público, mas não poderá atribuir um valor ao campo privado diretamente. Isso mantém a inicialização do rótulo em um só lugar. - 3ExemploAcrescente apenas o estado específico do sensor
class Sensor extends Device { private final String unit; Sensor(String label, String unit) { super(label); this.unit = unit; } public String unit() { return unit; } }Sensorestende uma classe e acrescenta um campo. Sua invocaçãosuper(label)passa a responsabilidade pelo rótulo aDevice; o próprio construtor do sensor armazena a unidade. - 4Ideia-chaveLeia ambas as partes por meio do sensor
Após
Sensor s = new Sensor("Room", "C");,s.label()retorna “Room” es.unit()retorna “C”. O primeiro método é herdado; o segundo pertence aSensor.Ambas as chamadas dizem respeito ao mesmo objeto. Estender
Devicenão criou um segundo objeto dispositivo ao lado do sensor. O rótulo privado está presente, embora o código do sensor o acesse por meio de um método. - 5DesafioEscolha a relação para um laboratório
Um
Labcontém vários sensores.Labdeve estenderSensorou manter referências a sensores? Explique a relação.---reveal---
Mantenha referências a sensores em campos ou em uma coleção. Um laboratório tem sensores, mas não é um sensor, portanto a composição é adequada. Estender
Sensorprometeria que um laboratório pode ser usado onde quer que um sensor seja necessário, inclusive em operações que descrevem a unidade de um sensor. - 6ExplicaçãoEntre no construtor da superclasse
Chamar
new Sensor("Room", "C")seleciona o construtor de sensor com dois argumentos. Sua chamadasuper(label)invocaDevice(String)para esse mesmo objeto.Deviceprimeiro chega à sua própria superclasse,Object, e depois atribui o rótulo. QuandoDeviceretorna, a inicialização dos campos do sensor e o restante do corpo do construtor do sensor são executados. Por fim, a nova referência fica disponível para quem fez a chamada. - 7DicaEncontre o construtor sem argumentos ausente
Remova
super(label)do construtor do sensor e Java inseresuper(). Essa chamada não tem correspondência:Devicedeclara apenasDevice(String).O erro do construtor diz respeito aos argumentos, não ao campo privado. Substituir a invocação por
new Device(label)cria um objeto sem relação e ainda deixa o problema dosuper()implícito. Passe o rótulo ao construtor da superclasse. - 8ExemploDelegue uma unidade padrão opcional
Acrescente este construtor dentro de
Sensor:Sensor(String label) { this(label, "unknown"); }new Sensor("Hall")delega ao construtor existente com dois argumentos, que invocasuper(label). O objeto resultante tem o rótulo “Hall” e a unidade “unknown”. Não escreva tambémsuper(...)no construtor que delega: um construtor tem no máximo uma invocação explícita de construtor. - 9ConexãoValide um parâmetro antes da construção
O Java 25 padrão permite trabalho restrito antes de uma invocação explícita de construtor. Por exemplo, um construtor de sensor pode rejeitar um rótulo recebido vazio antes de chamar
super(label).Esse código antecipado pode usar parâmetros, mas não pode ler os campos do objeto nem chamar seus métodos de instância. Atribuições simples restritas a campos da classe atual sem inicializadores na declaração também são permitidas. Para níveis padrão mais antigos da linguagem, mantenha a invocação em primeiro lugar.
- 10DesafioPreveja uma mensagem do construtor
Em uma versão separada,
Deviceexibe “device” após sua própria chamada à superclasse.Sensorexibe “sensor” apóssuper(label). Qual mensagem aparece primeiro ao criar um sensor e por quê?---reveal---
“device” aparece primeiro. O construtor do sensor invoca o construtor do dispositivo antes de continuar até sua própria instrução de impressão. O objeto é um sensor durante toda a construção; essas mensagens mostram a ordem do trabalho dos construtores, não mudanças em sua classe.
- 11DesafioCorrija uma atribuição a campo herdado
Uma nova subclasse tenta inicializar o rótulo privado do dispositivo com
this.label = label;e omitesuper(label). Quais dois problemas relacionados isso cria?---reveal---
A subclasse não pode atribuir um valor diretamente ao campo privado. O
super()implícito também não tem um construtor de dispositivo correspondente. Usesuper(label)para queDeviceatribua seu próprio estado privado e depois atribua valores apenas aos campos declarados na subclasse. - 12RecapitulaçãoLocalize uma falha de construção
Quando um subtipo não puder ser construído, localize a primeira chamada de construtor sem um destino acessível correspondente. Depois verifique se cada classe inicializa os campos pelos quais é responsável.
Mantenha o teste de relação junto desse rastreamento: um subtipo pode ser usado como seu tipo base; um objeto contido pertence a um campo. Essas verificações tratam de tipos diferentes de erros de projeto e de compilação.
2Exiba e compare entradas de catálogo corretamente
14 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoEscolha o que uma entrada de catálogo deve representar
- 2ExemploAmplie uma descrição básica
- 3DicaEvite chamar a sobrescrita de dentro dela mesma
- 4DesafioVerifique uma substituição aparente
- 5ExemploForneça à impressão um método exato para chamar
- 6Ideia-chaveIdentifique a classe real sem alterá-la
- 7DesafioDiagnostique texto exibido com aparência padrão
- 8ExemploDeclare os dados de igualdade
- 9ExemploRejeite o objeto errado antes de comparar campos
- 10ExemploCalcule o código de dispersão dos mesmos campos de igualdade
- 11DesafioIdentifique a sobrecarga que uma coleção não usa
- 12Ideia-chaveProteja comparações nas duas direções
- 13DesafioDistinga texto igual de valores iguais
- 14RecapitulaçãoVerifique uma classe de valor em sua interface pública
3Construa um contrato e compartilhe sua implementação
18 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoSepare os dados comuns da capacidade comum
- 2ExemploComece uma fonte abstrata de mensagens
- 3ExemploComplete a operação ausente
- 4DesafioDecida se uma subclasse pode ser concreta
- 5ExemploNomeie a leitura como uma capacidade
- 6ConexãoFaça a base existente cumprir a interface
- 7ExemploEscreva uma chamada usando o contrato
- 8DesafioCorrija uma implementação com acesso mais restrito
- 9ExemploAcrescente uma operação padrão útil
- 10ExplicaçãoMantenha os métodos auxiliares estáticos e privados em suas funções
- 11ExemploCrie um conflito real entre métodos padrão
- 12ExemploEscolha um método padrão explicitamente
- 13DesafioDê prioridade à implementação da classe
- 14Ideia-chaveOfereça uma base sem exigir que toda implementação a use
- 15DesafioColoque um rótulo e um construtor no tipo correto
- 16DicaVerifique novos métodos padrão em relação aos métodos herdados
- 17DesafioAcrescente um novo leitor sem mudar sua superclasse existente
- 18RecapitulaçãoAvalie uma proposta de tipo comum
4Acompanhe uma chamada de relatório por tipos gerais
17 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe o que a variável promete e o que o objeto faz
- 2ExemploDê uma referência geral a um relatório especializado
- 3ExplicaçãoVerifique o que a referência expõe
- 4DesafioEscolha tipos para uma lista mista de relatórios
- 5ExemploResolva primeiro a chamada externa
- 6ExemploResolva a chamada interna no mesmo objeto receptor
- 7ExemploEscolha uma sobrecarga pelo argumento declarado
- 8DesafioSepare duas decisões em uma chamada
- 9DicaMarque campos e chamadas estáticas separadamente em um rastreamento
- 10ExemploAcesse com segurança a operação exclusiva de vendas
- 11DesafioPreveja uma conversão explícita quando o objeto muda
- 12ExemploReúna cabeçalhos por meio da operação comum
- 13ConexãoUse comparação como outra operação compartilhada
- 14DesafioOrdene valores escolhendo uma implementação
- 15Ideia-chaveReserve verificações de subtipo para tarefas do subtipo
- 16DesafioOfereça um cabeçalho de auditoria sem um novo ramo
- 17RecapitulaçãoAcompanhe uma chamada desconhecida em ordem
5Construa uma opção fixa e um valor de dados protegido
15 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoEscolha entre opções nomeadas e dados agrupados
- 2ExemploDê às opções de serviço um tipo próprio
- 3ExemploAssocie números úteis às opções
- 4DicaMantenha os dados de serviço independentes da ordem de declaração
- 5DesafioPreveja uma busca e um campo útil
- 6ConexãoMantenha o comportamento com a opção fixa
- 7ExemploDeclare os componentes de uma solicitação de entrega
- 8ExemploNormalize os dados recebidos antes da atribuição
- 9DesafioCompare as solicitações normalizadas
- 10ExemploEncontre o objeto mutável dentro de um componente final
- 11ExemploArmazene uma cópia não modificável
- 12DicaRetorne um novo record quando os valores precisarem mudar
- 13DesafioEscolha a correção adequada para uma rota que muda
- 14DesafioModele novas opções de serviço e solicitações de clientes
- 15RecapitulaçãoExamine o comportamento gerado antes de acrescentar código
Questões de Herança, interfaces e polimorfismo em Java
Conjunto 1: Use contratos e tipos gerais de referência10 Questões de Prática
Questão 1 de 10
TextDocument e uma classe Image sem relação entre si devem oferecer exportText(). Cada uma já estende uma classe diferente. Qual tipo comum melhor expressa essa capacidade?Mostrar respostaOcultar resposta
Object não exporia exportText(), pois essa operação não existe em Object.Questão 2 de 10
Este código compila? Por quê?
interface Named { String name(); }
class Entity {
public String name() { return "entity"; }
}
class Asset extends Entity implements Named { }
Mostrar respostaOcultar resposta
Asset herda um name() público com a assinatura exigida por Named, portanto a classe concreta já cumpre o contrato. Sua superclasse não precisa nomear a interface. Exigir outra declaração em Asset ignoraria a implementação adequada que ela herda.Questão 3 de 10
O que new Screen().label() retorna?
interface Tagged {
default String label() { return "tagged"; }
}
class Panel {
public String label() { return "panel"; }
}
class Screen extends Panel implements Tagged { }
Mostrar respostaOcultar resposta
Panel tem prioridade sobre o método padrão correspondente da interface. Portanto, a tela retorna “panel”. A classe não precisa resolver esse caso como se fosse um conflito entre métodos padrão de duas interfaces sem relação entre si.Questão 4 de 10
Fast e Safe são interfaces sem relação entre si, cada uma com default String mode(). Uma Service concreta implementa ambas e não herda nenhuma implementação de mode() de uma classe. O que Service deve fazer?Mostrar respostaOcultar resposta
Service deve fornecer uma sobrescrita, que pode chamar um método padrão escolhido de uma interface direta com InterfaceName.super.mode(). Listar uma interface primeiro não dá prioridade ao seu método padrão.Questão 5 de 10
Robot implementa Movable. Em Movable unit = new Robot();, qual é o tipo declarado de unit?Mostrar respostaOcultar resposta
Movable. Robot é a classe real do objeto criado. Esses tipos têm funções diferentes: o tipo declarado limita as chamadas disponíveis, enquanto o objeto continua sendo um robô com suas implementações e seu estado.Questão 6 de 10
Vehicle tem move(), e apenas Bicycle tem ringBell(). Bicycle extends Vehicle. O que acontece com v.ringBell() após Vehicle v = new Bicycle();?Mostrar respostaOcultar resposta
Vehicle, que não tem ringBell(). O objeto bicicleta não perdeu seu método de campainha nem mudou de classe. Uma referência adequada de subtipo poderia acessá-lo, mas a vinculação dinâmica não pode fazer uma assinatura indisponível compilar.Questão 7 de 10
interface Percent declara static int cap(int n), e Gauge implements Percent não declara nenhum método cap. Como o código deve chamar o método auxiliar da interface com 120?Mostrar respostaOcultar resposta
Gauge.cap(120) nem uma chamada de instância alcança esse método auxiliar. Chamadas qualificadas com super de interface selecionam métodos padrão de instância, não métodos auxiliares estáticos.Questão 8 de 10
area(). Ele deve aceitar tanto ArrayList<Shape> quanto LinkedList<Shape>. Qual tipo de parâmetro expõe as operações de lista necessárias?Mostrar respostaOcultar resposta
List<Shape>, que permite percorrer elementos de forma. Escolher qualquer uma das implementações concretas rejeita quem usa a outra sem ajudar o laço. Object é geral demais porque não expõe as operações de lista de que o método precisa.Questão 9 de 10
O que acontece ao compilar e executar new Row().render()?
interface Renderable { String render(); }
abstract class Base implements Renderable {
public String render() { return "[" + body() + "]"; }
protected abstract String body();
}
class Row extends Base {
@Override protected String body() { return "row"; }
}
Mostrar respostaOcultar resposta
Base fornece a implementação pública da interface, mas deixa seu método auxiliar protegido abstrato. Row completa esse método auxiliar, portanto é concreta; o render() herdado chama a sobrescrita de Row e acrescenta os caracteres ao redor. O método auxiliar não precisa ser público porque não é a operação pública declarada por Renderable.Questão 10 de 10
Report abstrata com estado privado de título, um construtor que define esse estado e body() abstrato. Relatórios e objetos de gráficos sem relação com eles também devem poder ser impressos. Qual estrutura preserva ambos os requisitos?Mostrar respostaOcultar resposta
Conjunto 2: Construa classes e acompanhe comportamento herdado10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Library armazena vários objetos Shelf, e cada estante armazena livros. Como Library deve representar sua relação com Shelf?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Account declara um campo privado balance e um método público getBalance(). SavingsAccount extends Account. Como um método em SavingsAccount pode ler o estado herdado da conta?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
O que é exibido por new Leaf();?
class Root {
Root() { System.out.print("R"); }
}
class Branch extends Root {
Branch() { System.out.print("B"); }
}
class Leaf extends Branch {
Leaf() { this(7); System.out.print("L"); }
Leaf(int n) { System.out.print(n); }
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
O que acontece quando este código é compilado?
class Machine {
Machine(int serial) { }
}
class Printer extends Machine {
Printer() { }
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Meter declara public int read(). Uma subclasse escreve @Override public int read(int offset). Por que a compilação falha?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que a última instrução exibe?
class Parcel {
public String toString() { return "parcel"; }
}
class ChilledParcel extends Parcel {
@Override public String toString() {
return super.toString() + " cold";
}
}
Parcel item = new ChilledParcel();
System.out.println("Send " + item);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Label tem apenas public boolean equals(Label other), que compara seu texto, e herda equals(Object). Duas etiquetas distintas têm o mesmo texto. Ambas as referências são armazenadas em variáveis declaradas como Object. O que a.equals(b) retorna?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
abstract class Sensor declara public abstract double reading();. O que uma LightSensor extends Sensor concreta deve fornecer ou herdar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Por que esta classe não compila?
interface Resettable { void reset(); }
class Counter implements Resettable {
void reset() { }
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
O que new TimedJob().cost() retorna?
class Job {
public int rate() { return 3; }
public int cost() { return 4 * rate(); }
}
class TimedJob extends Job {
@Override public int rate() { return 5; }
@Override public int cost() { return super.cost() + 2; }
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Acompanhe chamadas, membros ocultados e conversões verificadas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Shape s = null; é seguido por s instanceof Circle. Circle extends Shape. Qual é o resultado do teste?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que acontece com a última instrução?
class Animal { }
class Fox extends Animal { }
Animal a = new Animal();
Fox f = (Fox) a;
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
O que pick(value) retorna?
static String pick(Object x) { return "object"; }
static String pick(CharSequence x) { return "sequence"; }
// Inside a method of the class containing these overloads:
Object value = "notes";
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Qual texto é produzido por p.label + ":" + p.show()?
class Parent {
String label = "P";
String show() { return label; }
}
class Child extends Parent {
String label = "C";
@Override String show() { return label; }
}
Parent p = new Child();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
List<Vehicle> e pode acrescentar um ônibus. Bicycle extends Vehicle e Bus extends Vehicle. Por que uma List<Bicycle> não pode ser passada a ele?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que b.test() retorna?
class Base {
String use(Object x) { return "BO"; }
String use(String x) { return "BS"; }
}
class Sub extends Base {
@Override String use(Object x) { return "SO"; }
@Override String use(String x) { return "SS"; }
String test() {
Object text = "x";
return super.use(text) + ":" + use(text);
}
}
Sub b = new Sub();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
O que new Sub(); exibe com o Java 25 padrão?
class Base {
Base() { System.out.print(value()); }
int value() { return -1; }
}
class Sub extends Base {
int n = 8;
Sub() { super(); n = 11; }
@Override int value() { return n; }
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
O que este código exibe? Tool não declara métodos.
class Tool { }
class Drill extends Tool {
int speed() { return 6; }
}
Tool tool = new Drill();
Object copy = tool;
if (copy instanceof Drill d) {
System.out.print(d.speed());
}
System.out.print(":" + (copy == tool));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Creator tem public Object create(). TextCreator extends Creator o sobrescreve. Qual combinação de tipo de retorno e acesso é válida?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Document. Seu método de prévia usa ramos instanceof para calcular a contagem de palavras de cada documento. Novas classes de documento são acrescentadas com frequência. Qual mudança permite que novas classes ofereçam prévia sem editar essa cadeia de testes de tipo?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Modele valores, compare-os e ordene-os10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
enum Status { NEW, SENT, DONE }. O que Status.valueOf("sent") faz?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
record Reading(int count, String unit) { }, qual chamada gerada lê a contagem de uma variável r?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
enum Band { LOW(2), HIGH(9); ... } armazena o argumento do construtor em um campo code imutável. Uma nova constante MID(5) é inserida entre LOW e HIGH. Como mudam o ordinal() e o código armazenado de HIGH?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Token diferentes retornam o mesmo código de dispersão. Suas implementações de equals(Object) comparam corretamente os campos que definem cada token. O código de dispersão compartilhado viola o contrato de igualdade e de código de dispersão?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
record Result(int seconds) implements Comparable<Result> retorna Integer.compare(seconds, other.seconds). Qual é o sinal de new Result(418).compareTo(new Result(405))?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que a última instrução exibe?
record Tag(String text) {
Tag { text = text.trim(); }
}
Tag a = new Tag(" north ");
Tag b = new Tag("north");
System.out.println(a.equals(b));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Ticket.equals da classe base aceita qualquer Ticket com o mesmo código. PriorityTicket extends Ticket aceita apenas objetos PriorityTicket e também compara a prioridade. Um bilhete comum t e um bilhete prioritário p têm o mesmo código. O que t.equals(p) e p.equals(t) retornam, e isso respeita a simetria?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
O que é exibido? List e ArrayList estão importados.
record Batch(List<String> names) { }
var original = new ArrayList<String>(List.of("Rin"));
Batch first = new Batch(original);
Batch second = new Batch(new ArrayList<>(original));
original.add("Sol");
System.out.println(first.equals(second) + ":"
+ first.names().size() + ":" + second.names().size());
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
O que acontece quando Collections.sort(list) é executado? Os tipos necessários de java.util estão importados.
record Rank(int score) implements Comparable<Rank> {
public int compareTo(Rank other) {
return Integer.compare(score, other.score);
}
}
List<Rank> list = List.of(new Rank(8), new Rank(3));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
O que Rule.DOUBLE.apply(Rule.NEXT.apply(4)) retorna?
enum Rule {
NEXT {
int apply(int n) {
return switch (n) { case 4 -> 6; default -> n + 1; };
}
},
DOUBLE { int apply(int n) { return 2 * n; } };
abstract int apply(int n);
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 5: Combine regras Java e diagnostique falhas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que new Terminal().name() retorna?
interface Basic {
default String name() { return "basic"; }
}
interface Detailed extends Basic {
default String name() { return "detail"; }
}
class Terminal implements Basic, Detailed { }
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
List<Notifier> contém objetos de várias classes sem relação entre si que implementam Notifier.send(). Um laço chama n.send() em cada elemento. Uma nova PushNotifier implementa o mesmo contrato corretamente. Qual mudança é necessária no laço para aceitar suas instâncias?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
O que é exibido com o Java 25 padrão?
class Base {
Base(int n) { System.out.print("B" + n); }
}
class Sub extends Base {
Sub(int n) {
if (n < 0) throw new IllegalArgumentException();
super(n + 1);
System.out.print("S" + n);
}
}
new Sub(2);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Code define igualdade e cálculo de código de dispersão usando seu campo de texto. Um HashSet<Code> contém c com o texto “red”. O programa deve mudar seu texto para “blue”, manter o mesmo objeto e preservar uma busca confiável no conjunto. Em que ordem ele deve atualizar o objeto e o conjunto?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que x.call(arg) + ":" + x.call("x") produz?
class Alpha {
String call(Object x) { return "AO"; }
String call(String x) { return "AS"; }
}
class Beta extends Alpha {
@Override String call(Object x) { return "BO"; }
@Override String call(String x) { return "BS"; }
}
Alpha x = new Beta();
Object arg = "data";
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
O que a última instrução exibe? List e ArrayList estão importados.
record Group(List<String> names) {
Group { names = List.copyOf(names); }
}
var original = new ArrayList<String>();
original.add("Uma");
Group group = new Group(original);
original.add("Zed");
System.out.println(group.names().size() + ":" + original.size());
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
O que acontece quando este código é compilado?
interface Ready {
default String state() { return "ready"; }
}
abstract class Base {
public abstract String state();
}
class Console extends Base implements Ready { }
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
record Player(String name, int points) têm nomes diferentes, mas a mesma pontuação. Seu compareTo personalizado compara apenas a pontuação usando Integer.compare. O que equals e compareTo informam para esses dois objetos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
interface Storable { String saveText(); }. Muitas novas implementações compartilham um campo privado de caminho, validação no construtor e código auxiliar, enquanto algumas implementações existentes estendem outras classes. Como a biblioteca pode oferecer reutilização preservando a API comum?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Acesso protegido
Anotação @Override
@Override, pede ao compilador que verifique se um método sobrescreve ou implementa um método herdado.tostring() quando quem programou pretendia substituir toString().Assinatura do método
Object para String altera a assinatura do método e cria uma sobrecarga.C
Cadeia de construtores
Capacidade
Classe abstrata
Shape pode armazenar um nome enquanto exige que círculos e retângulos concretos forneçam uma área.Classe concreta
Rectangle é uma classe concreta quando completa a operação de área exigida por sua base abstrata de forma.Classe em tempo de execução
getClass() independentemente do tipo declarado de sua referência.Student mesmo quando um método o recebe por meio de um parâmetro Person.Código de dispersão
Colisão de hash
Componente de record
seconds pode ser lido de um corredor usando o método gerado runner.seconds().Composição
Customer contenha um Address sem fazer com que todo cliente possa ser usado como endereço.Conflito entre métodos padrão
SmartLamp sobrescreva as descrições correspondentes de Lamp e Speaker.Constante de enumeração
CupSize.LARGE pode armazenar uma capacidade de 500 mL e ser comparada por igualdade de identidade.Constante de interface
Chargeable.FULL pode conter 100 enquanto cada dispositivo armazena seu próprio campo variável de bateria.Construtor canônico
Point(int x, int y) aceita a coordenada x seguida da coordenada y.Construtor compacto
Construtor padrão
super() em seu corpo.Person(String name) remove o construtor padrão do qual uma subclasse pode ter dependido.Contrato de igualdade
Conversão para subtipo
Conversão para supertipo
Chargeable mantém seus dados de bateria, mas expõe apenas as operações disponíveis por meio desse tipo.Cópia não modificável
List.copyOf protege a lista de membros de uma equipe contra acréscimos à lista mutável de quem fez a chamada.Corpo específico de constante
MULTIPLY implemente uma operação de modo diferente da constante ADD do enum.Corpo flexível de construtor
E
Empate na ordenação
compareTo retorna zero, indicando que dois objetos ocupam a mesma posição de comparação segundo a regra de ordenação escolhida.Estado compartilhado
Estouro de inteiro
Integer.compare evita esse erro de comparação.Exceção ClassCastException
Animal comum é convertido explicitamente para sua subclasse mais específica Fox.Expressão switch
H
Herança
Student reutilize Person.getName() enquanto acrescenta seus próprios créditos e comportamento de estudo.Herança simples
Student quanto Teacher como superclasses diretas.I
Identidade
a == b ser falso.Igualdade por valor
Implementação
Entity pode cumprir Named.name() para uma subclasse Asset.Implementação parcial
Storable pode compartilhar a validação de caminho enquanto classes concretas fornecem o texto salvo.Imutabilidade superficial
Inicializador de campo
Interface
Chargeable permite que um telefone e uma scooter ofereçam as mesmas chamadas enquanto carregam em ritmos diferentes.Interface Comparable
compareTo.Comparable<Runner> para que um método de ordenação possa organizar corredores por tempo de chegada.Interface mais específica
Detailed fornece o name() escolhido quando estende Basic e substitui seu método padrão.Invocação do construtor da superclasse
super(...) para passar argumentos a um construtor da superclasse direta para o objeto atual.Person, responsável pelo campo privado de nome.M
Método abstrato
area() impede que uma forma concreta incompleta use silenciosamente um método provisório que retorna área zero.Método concreto
Shape.describe() pode reutilizar o nome e chamar a implementação de area() de cada subclasse.Método de acesso
count é count(), portanto getCount() não está disponível a menos que seja declarado explicitamente.Método estático de interface
Chargeable.clamp(130) pode retornar 100 sem criar um objeto de dispositivo.Método final
Método padrão
needsCharge() pode usar batteryPercent() sem conhecer a organização dos campos privados de nenhum dispositivo.Método privado de interface
O
Object
Object pode conter um estudante ou um vetor, e ambos oferecem os métodos que todo objeto possui.Objeto receptor
shape.area(), o objeto receptor é o objeto de forma, mesmo quando a variável tem um tipo declarado abstrato.Ocultação de campo
p.label ler o campo da classe pai enquanto p.show() executa uma sobrescrita da classe filha que usa outro rótulo.Ocultação de método
Operador instanceof
instanceof permite que o código verifique se uma pessoa tem o tipo de estudante antes de usar um método exclusivo de estudante.Ordem natural
P
Polimorfismo
area() em círculos, retângulos e triângulos sem ramos separados por tipo.Posição ordinal
LOW, HIGH, a posição ordinal de HIGH muda de 1 para 2 quando MID é inserido antes dela.R
Registro
Point(int x, int y) agrupa duas coordenadas e fornece igualdade baseada nos componentes sem métodos escritos à mão.Representação textual
toString(), usada quando o objeto é exibido ou unido a outro texto.S
Simetria
book.equals(ebook) é verdadeiro, mas ebook.equals(book) é falso para esses mesmos objetos.Sobrecarga
print(String text) ao lado de print(int number) sem substituir nenhum dos métodos.Sobrescrita
yearlyPay() permite que um gerente acrescente um bônus enquanto quem faz chamadas continua usando a operação de funcionário.Subclasse
GraduateStudent pode se basear em Student e ainda ser usada onde se espera uma Person.Superclasse
Person é a superclasse de Student, portanto um método que aceita uma pessoa também pode receber um estudante.Superinterface
Chargeable é uma superinterface de SmartDevice quando esta a estende junto com Connectable.T
Tipo de interface
List como tipo de interface permite que um método aceite tanto listas baseadas em vetor quanto listas encadeadas.Tipo de retorno covariante
String quando o método da superclasse retorna Object.Tipo declarado
Person p = new Student(...), o tipo declarado é Person, portanto métodos exclusivos de estudante não estão disponíveis diretamente.Tipo enumerado
NEW, SENT e DONE.Tipo real
Person pode apontar para um estudante cujo tipo real continua sendo Student após a conversão para supertipo.Transitividade
V
Variável de padrão
student em person instanceof Student student expõe com segurança o método de identificação do objeto correspondente.Vinculação dinâmica
hello() herdado que chama who() chegue a B.who() quando o objeto receptor pertence à classe B.- 1Primeiros programas em Java
- 2Condições e laços em Java
- 3Métodos em Java
- 4Listas e vetores em Java
- 5Classes e objetos em Java
- 6Objetos e referências em Java
- 7HashMaps e coleções em Java
- 8Herança, interfaces e polimorfismo em Java
- 9Exceções em Java
- 10Pacotes e testes unitários em Java
- 11Genéricos e expressões lambda em Java
Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.