필수 인자부터 시작하여 선택적 인자를 단계적으로 늘린 생성자들을 오버로딩하는 방식
public class Coffee {
private final String type; // 필수
private final boolean sugar; // 선택
private final boolean milk; // 선택
private final String topping; // 선택
public Coffee(String type) {
this(type, false, false, null);
}
public Coffee(String type, boolean sugar) {
this(type, sugar, false, null);
}
public Coffee(String type, boolean sugar, boolean milk) {
this(type, sugar, milk, null);
}
public Coffee(String type, boolean sugar, boolean milk, String topping) {
this.type = type;
this.sugar = sugar;
this.milk = milk;
this.topping = topping;
}
}
장점
단점
new Coffee("Latte", false, true)에서 false가 설탕인지 우유인지 불분명기본 생성자로 생성 후 setter로 속성 설정
Car car = new Car();
car.setModel("Sonata");
car.setYear(2025);
car.setColor("White");
장점
단점
Copublic class Computer {
private final String cpu; // 필수
private final int ramGB; // 필수
private final String storage; // 선택
private final String graphicsCard; // 선택
// private 생성자로 외부에서 직접 객체 생성 방지
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ramGB = builder.ramGB;
this.storage = builder.storage;
this.graphicsCard = builder.graphicsCard;
}
public static class Builder {
private final String cpu;
private final int ramGB;
private String storage = "256GB SSD"; // 기본값
private String graphicsCard = "Integrated"; // 기본값
// 필수 매개변수를 받는 빌더 생성자
public Builder(String cpu, int ramGB) {
this.cpu = cpu;
this.ramGB = ramGB;
}
public Builder storage(String storage) {
this.storage = storage;
return this; // 빌더 자신을 반환하여 체이닝 가능
}
public Builder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
// 사용 예시
Computer gamingPC = new Computer.Builder("Intel i9", 32)
.storage("1TB NVMe SSD")
.graphicsCard("NVIDIA RTX 4080")
.build();
Computer officePC = new Computer.Builder("Intel i5", 16)
.build(); // 선택 매개변수 생략 시 기본값 사용
장점
단점
List<B>도 List<A>의 서브타입으로 간주특정 타입이 다른 타입의 기능을 모두 포함하거나 확장하는 관계
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
Animal myAnimal = new Dog();
클래스나 메서드에서 사용할 타입을 일반화하는 방법
List<Animal> animals = new ArrayList<>();
animals.add(new Animal());
animals.add(new Dog());
animals.add(new Cat());
// Dog 배열을 Animal 배열에 할당가능 -> 런타임 오류의 가능성 내포
Animal[] animals = new Dog[10];
// ArrayStoreException이 발생 가능 -> 실제 배열은 Dog 타입만 담을 수 있음
animals[0] = new Cat();
Java의 제네릭은 기본적으로 불공변성
List<Dog> dogList = new ArrayList<>();
List<Animal> animalList = dogList; // 컴파일 오류
List<Dog> dogList = new ArrayList<>();
List<? extends Animal> animalList = dogList; // 가능
// animalList.add(new Cat()); // 컴파일 오류
오버라이드하는 메서드의 반환 타입이 오버라이드되는 메서드의 반환 타입의 서브타입이 될 수 있도록 허용하는 특성
class Animal {
public Animal produce() {
System.out.println("동물 객체 생성");
return new Animal();
}
}
class Dog extends Animal {
@Override
public Dog produce() { // 공변 반환 타이핑
System.out.println("개 객체 생성");
return new Dog();
}
}
class Cat extends Animal {
@Override
public Cat produce() {
System.out.println("고양이 객체 생성");
return new Cat();
}
}
Animal animalProducer = new Animal();
Dog dogProducer = new Dog();
Cat catProducer = new Cat();
Animal a = animalProducer.produce(); // 반환 타입은 Animal
Dog d = dogProducer.produce(); // 반환 타입은 Dog (캐스팅 필요 없음)
Cat c = catProducer.produce(); // 반환 타입은 Cat (캐스팅 필요 없음)
// 다형성
Animal polyProducer = new Dog();
// polyProducer.produce()의 실제 반환 타입은 Dog이지만,
// 컴파일러는 Animal 타입으로 간주하므로 Dog 타입으로 받으려면 캐스팅 필요
Dog d2 = (Dog) polyProducer.produce();