Olá! Nas palestras anteriores você já aprendeu como criar suas próprias classes completas, com campos e métodos. Este é um progresso sério, muito bem! Mas agora tenho que lhe contar uma verdade desagradável. Não criamos nossas aulas muito bem! Por que? À primeira vista, não há erros nesta classe:
O nome vem do inglês “ get ” - “ receber ” (ou seja, “método para obter o valor de um campo”) e set - “ set ” (ou seja, “método para definir o valor de um campo”). Vamos ver como eles ficam usando nossa classe como exemplo
Uma analogia pode ser feita com um telefone celular. Imagine que em vez de um celular normal ligado, você recebeu um telefone com a caixa aberta, onde estão todos os fios, circuitos, etc. saindo. O telefone funciona: se você se esforçar e mexer nos diagramas, poderá até conseguir fazer uma ligação. Mas você provavelmente vai quebrá-lo. Em vez disso, a empresa fabricante oferece uma interface: o cliente simplesmente disca os números necessários, pressiona o botão verde do aparelho e a chamada começa. E ele não se importa com o que está acontecendo lá dentro com os circuitos e fios e como eles realizam sua tarefa. Neste exemplo, a empresa limitou o acesso aos “interiores” (dados) do telefone e deixou apenas a interface (métodos) de fora. Como resultado, o cliente conseguirá o que deseja (fazer uma ligação) e definitivamente não quebrará nada por dentro.
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
Na verdade, existe. Imagine que enquanto está sentado no trabalho você escreveu uma classe como esta Cat
, denotando gatos. E ele foi para casa. Enquanto você estava fora, outro programador veio trabalhar, criou sua própria classe Main
, onde começou a usar a classe que você escreveu Cat
.
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Não importa por que ele fez isso ou como aconteceu: talvez a pessoa estivesse cansada ou não dormisse o suficiente. Outra coisa é importante: nossa classe atual Cat
permite atribuir valores malucos aos campos. Como resultado, o programa contém objetos com estado incorreto, como este gato com idade de -1000 anos. Que erro acabamos cometendo? Quando criamos a classe, expomos seus dados. Campos name
e age
são weight
de domínio público. Eles podem ser acessados em qualquer lugar do programa: basta criar um objeto Cat
– e pronto, qualquer programador tem acesso aos seus dados diretamente através do operador “ .
”
Cat cat = new Cat();
cat.name = "";
Aqui acessamos diretamente o campo name
e definimos seu valor. Precisamos proteger de alguma forma nossos dados contra interferências externas incorretas. O que é necessário para isso? Primeiro, todas as variáveis de instância (campos) devem ser marcadas com um modificador private
. Private é o modificador de acesso mais estrito em Java. Se você usá-lo, os campos da classe Cat
não estarão acessíveis fora dela.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";//error! The name field in the Cat class has private access!
}
}
O compilador vê isso e imediatamente produz um erro. Agora os campos parecem estar protegidos. Mas acontece que o acesso a eles está “hermeticamente” fechado: o programa não consegue nem pesar o peso de um gato existente, se necessário. Isso também não é uma opção: dessa forma nossa classe é praticamente impossível de usar. Idealmente, precisamos permitir algum tipo de acesso limitado aos dados:
- Outros programadores deveriam ser capazes de criar objetos
Cat
- Eles devem ser capazes de ler dados de objetos já existentes (por exemplo, obter o nome ou a idade de um gato já existente).
- Também deve ser possível atribuir valores de campo. Mas ao mesmo tempo - apenas valores corretos. Nossos objetos devem ser protegidos de itens incorretos (sem “idade = -1000 anos” e similares).
Cat
:
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Como você pode ver, tudo é bem simples :) Seus nomes geralmente consistem na palavra get/set + o nome do campo pelo qual são responsáveis. Por exemplo, um método getWeight()
retorna o valor de um campo weight
para o objeto para o qual foi chamado. Isto é o que parece no programa:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
String barsikName = barsik.getName();
int barsikAge = barsik.getAge();
int barsikWeight = barsik.getWeight();
System.out.println("Cat name: " + barsikName);
System.out.println("Cat's age: " + barsikAge);
System.out.println("Weight of the cat: " + barsikWeight);
}
}
Saída do console:
Name кота: Барсик
Возраст кота: 5
Вес кота: 4
Agora de outra classe( Main
) há acesso aos campos Cat
, mas apenas através de getters . Observe que os getters possuem um modificador de acesso public
, o que significa que podem ser acessados de qualquer lugar do programa. E quanto à atribuição de valores? Os métodos setter são responsáveis por isso
public void setName(String name) {
this.name = name;
}
O trabalho deles, como você pode ver, também é simples. Chamamos um método setName()
em um objeto Cat
, passamos uma string como argumento e essa string é atribuída a um campo name
do nosso objeto.
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
System.out.println("The original name of the cat is " + barsik.getName());
barsik.setName("Basil");
System.out.println("The new name of the cat -" + barsik.getName());
}
}
Aqui usamos getters e setters. Primeiro, usando um getter, recebemos e enviamos para o console o nome inicial do gato. Então, usando um setter, name
um novo valor foi atribuído ao seu campo - “Vasily”. E então, usando um getter, pegamos o nome novamente para verificar se realmente mudou. Saída do console:
Изначальное Name кота — Барсик
Новое Name кота — Васorй
Ao que parece, qual é a diferença? Também podemos atribuir valores incorretos aos campos dos objetos, mesmo que tenhamos setters:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
Saída do console:
Возраст Барсика — -1000 лет
A diferença é que um setter é um método completo . E em um método, diferentemente de um campo, você pode incluir a lógica de verificação necessária para evitar valores inaceitáveis. Por exemplo, você pode facilmente desativar a atribuição de um número negativo como idade:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Error! Age cannot be negative!");
}
}
E agora nosso código funciona corretamente!
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
Saída do console:
Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика — 5 лет
Existe uma restrição dentro do setter e ela protege contra tentativas de configuração de dados incorretos. A idade de Barsik permaneceu inalterada. Getters e setters sempre devem ser criados. Mesmo que seus campos não tenham restrições de valores possíveis, não haverá nenhum dano com eles. Imagine uma situação: você e seus colegas estão escrevendo um programa juntos. Você criou uma classe Cat
com campos públicos e todos os programadores os utilizam como desejam. E então, um belo dia, você percebe: “Droga, mais cedo ou mais tarde alguém pode acidentalmente atribuir um número negativo a uma variável weight
! Precisamos criar setters e tornar todos os campos privados!” Você os cria e todo o código escrito por seus colegas quebra instantaneamente. Afinal, eles já haviam escrito um monte de código onde acessavam os campos Cat
diretamente.
cat.name = "Hippopotamus";
E agora os campos se tornaram privados e o compilador produz vários erros!
cat.name = "Hippopotamus";//error! The name field of the Cat class has private access!
Em tal situação, seria melhor ocultar os campos e criar getter-setters desde o início . Todos os seus colegas os usariam e, se você percebesse tarde que precisava limitar os valores dos campos, bastaria adicionar uma verificação dentro do setter. E ninguém quebraria o código já escrito. Claro, se quiser acesso somente leitura a um determinado campo, você pode criar um getter para ele. “Fora”, ou seja, fora da sua classe, apenas métodos deverão estar disponíveis. Os dados devem estar ocultos.
GO TO FULL VERSION