하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
ArrayList<? extends Product> list = new ArrayList<Tv>();
ArrayList<? extends Product> list = new ArrayList<Audio>();
ArrayList<Product> list = new ArrayList<Tv>();
- <? extends T >
- 와일드 카드의 상한 제한. T와 그 자손들만 가능
- <? super T>
- 와일드 카드의 하한 제한. T와 그 조상들만 가능
- <?>
- 제한 없음. 모든 타입이 가능. ? extends Object와 동일
메소드의 매개변수에 와일드 카드를 사용
package ch02;
import java.util.*;
class Fruit { public String toString() { return "Fruit"; }}
class Apple extends Fruit { public String toString() { return "Apple"; }}
class Grape extends Fruit { public String toString() { return "Grape"; }}
class Juice {
String name;
Juice(String name) { this.name = name + "Juice";}
public String toString() { return name; }
}
class Juicer {
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit f : box.getList())
tmp += f + " ";
return new Juice(tmp);
}
}
public class EX3 {
public static void main(String[] args) {
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple());
appleBox.add(new Apple());
System.out.println(Juicer.makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox));
}
}
class FruitBox<T extends Fruit> extends Box<T> {}
class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
ArrayList<T> getList() { return list; }
int size() { return list.size(); }
public String toString() { return list.toString(); }
}
Apple Grape Juice
Apple Apple Juice
지네릭 타입이 선언된 메소드(타입 변수는 메소드 내에서만 유효)
static <T> void sort(List<T> list, Comparator<? super T> c)
클래스의 타입 매개변수와 메소드의 타입 매개변수 는 별개
class FruitBox<T> {
...
static <T> void sort(List<T> list, Comparator<? super T> c>){
... // 타입 문자가 일치하지만 다른 타입 변수가 사용된다.
}
}
메소드를 호출할 때마다 타입을 대입해야 한다.
메소드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략 불가
System.out.println(<Fruit>makeJuice(fruitBox)); // 에러
System.out.println(this.<Fruit>makeJuice(fruitBox));
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
지네릭 타입과 원시 타입 간의 형변환은 바람직 하지 않다.(경고 발생)
Box<Object> objBox = null;
Box box = (Box)objBox; // 지네릭 타입 -> 원시 타입, 실행은 되나 경고 발생
objBox = (Box<Object>)box; // 원시타입 -> 지네릭 타입, 실행은 되나 경고 발생
objBox = (Box<Object>)strBox; // 에러
strBox = (Box<String>)objBox; // 에러
와일드 카드가 사용된 지네릭 타입으로는 형변환 가능
Box<Object> objBox = (Box<Object>)new Box<String>(); // 에러
Box<? extends Object> wBox = (Box<? extends Object>)new Box<String>();
Box<? extends Object> wBox = new Box<String>(); // 위 문장과 동일하다.
컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣는다. (하위 호환성을 위해)
- 지네릭 타입의 경계(bound)를 제거
class Box<T extends Fruit> {
void add(T t) {
...
}
}
class Box {
void add(Fruit t) {
...
}
}
- 지네릭 타입 제거 후에 타입이 불일치하면, 형변환을 추가
T get(int i) {
return list.get(i);
}
Fruit get(int i) {
return (Fruit)list.get(i);
}
- 와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit f : box.getList())
tmp += f + " ";
return new Juice(tmp);
}
static Juice makeJuice(FruitBox box) {
String tmp = "";
Iterator it = box.getList().iterator();
whiel(it.hasNext()) {
tmp += (Fruit)it.next() + " ";
}
return new Juice(tmp);
}
관련된 상수들을 같이 묶어 놓은 것. Java는 타입에 안전한 열거형을 제공
Java의 열거형은 타입을 모두 체크한다는 뜻이다.
class Card { // 0 1 2 3
enum Kind {CLOVER, HEART, DIAMOND, SPADE} // 자동으로 0부터 대입된다.
enum Value {TWO, THREE, FOUR}
final Kind Kind; // 타입이 int가 아닌 Kind임에 유의
final Value value;
}
열거형을 정의하는 법
enum 열거형이름 { 상수명1, 상수명2, ... }
열거형 타입의 변수를 선언하고 사용하는 법
enum Direction { EAST, SOUTH, WEST, NORTH }
class Unit {
int x, y;
Direction dir; // 열거형 인스턴스 변수를 선언
void init() {
dir = Direction.EAST // 유닛의 방향을 EAST로 초기화
}
}
열거형 상수의 비교에 ==와 compareTo() 사용가능
if(dir==Direction.EAST){
x++;
} else if (dir > Direction.WEST) { // 에러. 열거형 상수에 비교연산자 사용불가
...
} else if (dir.compareTo(Direction.WEST) > 0){ // compareTo()는 가능
...
}
모든 열거형은 Enum의 자손이며, 아래의 메소드를 상속받는다.
메소드 | 설명 |
---|---|
Class getDeclaringClass() | 열거형의 Class객체를 반환 |
String name() | 열거형 상수의 이름을 문자열로 반환 |
int ordinal() | 열거형 상수가 정의된 순서를 반환(0부터 시작) |
T valueOf(Class enumType, String name) | 지정된 열거형에서 name과 일치하는 열거형 상수를 반환 |
values(), valueOf()는 컴파일러가 자동으로 추가
static E[] values()
static E valueOf(String name)
Direction[] dArr = Direction.values();
for(Direction d : dArr) // for(Direction d : Direction.values())
System.out.printf("%s=%d%n", d.name(), d.ordinal());
예제
package ch02;
import java.util.*;
enum Direction { EAST, SOUTH, WEST, NORTH }
public class EX3 {
public static void main(String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
System.out.println("d1="+d1);
System.out.println("d2="+d2);
System.out.println("d3="+d3);
System.out.println("d1==d2 ? " + (d1==d2));
System.out.println("d1==d3 ? " + (d1==d3));
System.out.println("d1.equals(d3) ? "+d1.equals(d3));
System.out.println("d1.compareTo(d3) ? "+(d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ? "+(d1.compareTo(d2)));
switch(d1) {
case EAST: // Direction.EAST라고 쓸 수 없다.
System.out.println("The direction is EAST."); break;
case SOUTH:
System.out.println("The direction is SOUTH."); break;
case WEST:
System.out.println("The direction is WEST."); break;
case NORTH:
System.out.println("The direction is NORTH."); break;
default:
System.out.println("Invalid direction."); break;
}
Direction[] dArr = Direction.values(); // 열거형의 모든 상수를 배열로 반환
for(Direction d : dArr)
System.out.printf("%s=%d%n", d.name(), d.ordinal());
}
}
d1=EAST
d2=WEST
d3=EAST
d1==d2 ? false
d1==d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2
The direction is EAST.
EAST=0
SOUTH=1
WEST=2
NORTH=3
불연속적인 열거형 상수의 경우, 원하는 값을 괄호안에 넣는다. 괄호 안에 여러 개를 넣는 것도 가능하다.
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }
괄호를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야 한다.
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 끝에 세미콜론 추가
private final int value; // 정수를 저장할 필드(인스턴스 변수)를 추가
Direction(int value) { this.value = value; } // 생성자 추가
public int getValue() { return value; }
}
열거형의 생성자는 묵시적으로 private이므로, 외부에서 객체 생성 불가
Direction d = new Direction(1); // 에러
예시
package ch02;
import java.util.*;
enum Direction {
EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4, "^"); // 끝에 세미콜론 추가
private static final Direction[] DIR_ARR = Direction.values();
private final int value;
private final String symbol;
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
public int getValue() { return value; }
public String getSymbol() {return symbol; }
public static Direction of(int dir) {
if (dir < 1 || dir > 4)
throw new IllegalArgumentException("Invalid value : " + dir);
return DIR_ARR[dir-1];
}
// 방향을 회전시키는 메소드. num의 값만큼 90도씩 시계방향으로 회전한다.
public Direction rotate(int num) {
num = num % 4;
if(num < 0) num += 4; // num이 음수일 때는 시계반대 방향으로 회전
return DIR_ARR[(value-1+num) % 4];
}
public String toString() {
return name()+getSymbol();
}
}
public class EX3 {
public static void main(String[] args) {
for(Direction d : Direction.values())
System.out.printf("%s=%d%n", d.name(), d.getValue());
Direction d1 = Direction.EAST;
Direction d2 = Direction.of(1);
System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
System.out.println(Direction.EAST.rotate(1));
System.out.println(Direction.EAST.rotate(2));
System.out.println(Direction.EAST.rotate(-1));
System.out.println(Direction.EAST.rotate(-2));
}
}
EAST=1
SOUTH=2
WEST=3
NORTH=4
d1=EAST, 1
d2=EAST, 1
SOUTHV
WEST<
NORTH^
WEST<
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String buffer = s.nextLine();
System.out.println(buffer+"??!");
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int buffer = s.nextInt();
System.out.println(buffer-543);
}
}
package ch02;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int x = s.nextInt();
int y = s.nextInt();
int answer = (x > 0 ? (y > 0 ? 1 : 4) : (y > 0 ? 2 : 3));
System.out.println(answer);
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int h = s.nextInt();
int m = s.nextInt();
int duration = s.nextInt();
m += duration;
if (m >= 60) {
h += m / 60;
m %= 60;
}
if (h >= 24) {
h -= 24;
}
System.out.printf("%d %d", h, m);
}
}
package ch02;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int dice1 = s.nextInt();
int dice2 = s.nextInt();
int dice3 = s.nextInt();
int reward = 0;
if (dice1 == dice2 && dice1 == dice3) {
reward = 10000 + dice1*1000;
} else if (dice1 != dice2 && dice1 != dice3 && dice2 != dice3) {
if (dice1 > dice2) {
if(dice1 > dice3) {
reward = dice1*100;
} else {
reward = dice3*100;
}
} else {
if (dice2 > dice3) {
reward = dice2*100;
} else {
reward = dice3*100;
}
}
} else {
if(dice1 == dice2) {
reward = 1000 + dice1*100;
} else if (dice1 == dice3) {
reward = 1000 + dice1*100;
} else {
reward = 1000 + dice2*100;
}
}
System.out.println(reward);
}
}
조금 보기 쉽게 손 본 버전
package ch02;
import java.util.*;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> diceBox = new ArrayList<>(3);
getDice(diceBox);
diceBox.sort(Comparator.naturalOrder());
int reward = 0;
int result = 0;
Set<Integer> s = new HashSet<>(diceBox);
if(s.size() == 1)
result = 0;
else if(s.size() == 2)
result = 1;
else
result = 2;
reward = getReward(diceBox, result);
System.out.println(reward);
}
public static int getReward(ArrayList<Integer> diceBox, int winType) {
int reward;
switch(winType) {
case 0:
reward = 10000 + diceBox.get(0) * 1000;
break;
case 1:
if(diceBox.get(0).equals(diceBox.get(1)))
reward = 1000 + diceBox.get(0)*100;
else
reward = 1000 + diceBox.get(1)*100;
break;
case 2:
reward = diceBox.get(2)*100;
break;
default:
reward = 0;
}
return reward;
}
public static void getDice(ArrayList diceBox) {
Scanner s = new Scanner(System.in);
for(int i = 0; i<3; i++) {
diceBox.add(s.nextInt());
}
}
}