import java.util.Arrays;
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + ": " + age;
}
}
class ArrayObjSearch {
public static void main(String[] args) {
Person[] ar = new Person[3];
ar[0] = new Person("Lee", 29);
ar[1] = new Person("Goo", 15);
ar[2] = new Person("Soo", 37);
}
}
package com.test.memo;
import java.util.Arrays;
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + ": " + age;
}
@Override
public int compareTo(Person o) {
return name.compareTo(o.name);
}
}
public class Practice2 {
public static void main(String[] args) {
Person[] ar = new Person[3];
ar[0] = new Person("Cee", 29);
ar[1] = new Person("Aoo", 15);
ar[2] = new Person("Boo", 37);
Arrays.sort(ar);
int idx = Arrays.binarySearch(ar, new Person("Cee", 29));
System.out.println(idx);
System.out.println(ar[idx]);
}
}

package com.test.memo;
import java.math.BigDecimal;
import java.util.Scanner;
public class Practice2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("첫번째 실수값: "); // BigDecimal이 문자열로 저장하니까
String num1 = sc.nextLine();
System.out.print("두번째 실수값: ");
String num2 = sc.nextLine();
BigDecimal b1 = new BigDecimal(num1);
BigDecimal b2 = new BigDecimal(num2);
BigDecimal result = (b1.subtract(b2));
System.out.println(result.abs());
}
}
package com.test.memo;
import java.math.BigInteger;
public class Practice2 {
public static void main(String[] args) {
BigInteger num1 = new BigInteger("100000000000000000000");
BigInteger num2 = new BigInteger("-99999999999999999999");
System.out.println("두 수를 더한 결과: " + num1.add(num2));
System.out.println("두 수를 곱한 결과" + num1.multiply(num2));
}
}

package com.test.memo;
import java.math.BigDecimal;
public class Practice2 {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("1.6");
BigDecimal num2 = new BigDecimal("0.1");
System.out.println("더한 값: " + num1.add(num2));
System.out.println("곱한 값: " + num1.multiply(num2));
}
}

컴퓨터의 현재시간을 기준으로, 1970년 1월1일 자정 이후로 지나온 시간을 밀리 초(1/1000초) 단위로 계산해서 반환하는 메소드는?
System.currentTimeMillisAPI 문서에서는 "Random의 생성자가 호출될 때, 이전에 호출될 때와 전혀 다른 씨드 값이 설정된다.
Math 클래스에 static으로 선언되어 있는 random 메소드를 이용해서 난수를 생성하는 방법도 존재한다.
Math 클래스에 정의되어 있는 random 메소드는 Random 클래스의 nextDouble 메소드와 마찬가지로 0.0 이상 1.0 미만의 double 형 난수를 반환한다.
nextToken()hasMoreTokens()package com.test.memo;
import java.util.StringTokenizer;
public class Practice2 {
public static void main(String[] args) {
String time = "11:22:33:44:55";
StringTokenizer st = new StringTokenizer(time, ":");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
}
}
아무 생각없이 st를 바로 출력했더니 참조값 주소가 나왔다.
hasMoreTokens()를 이용해서 반복문 돌려서 토큰을 차례로 출력해줘야 한다."TEL 82-02-997-2059"를 " "을 구분자로 하여 각각의 토큰을 출력하시오.
"TEL 82-02-997-2059"를 " -"을 구분자로 하여 각각의 토큰을 출력하시오.
"num+=1"를 "+="을 구분자로 하여 각각의 토큰을 출력하시오. 구분자도 토큰으로 인식하도록하여 출력하자.
package com.test.memo;
import java.util.StringTokenizer;
public class Practice2 {
public static void main(String[] args) {
String time = "TEL 82-02-997-2059";
String num = "num+=1";
StringTokenizer first = new StringTokenizer(time);// 공백을 구분자로
StringTokenizer twice = new StringTokenizer(time, " -"); // 공백과 하이폰을 구분자로
StringTokenizer third = new StringTokenizer(num, "+=", true); // 구분자포함 토큰으로 인식
while (first.hasMoreTokens()) {
System.out.print(first.nextToken() + " ");
}
System.out.println();
while (twice.hasMoreTokens()) {
System.out.print(twice.nextToken() + " ");
}
System.out.println();
while (third.hasMoreTokens()) {
System.out.print(third.nextToken() + " ");
}
}
}

27일 메모 참조
package com.test.memo;
public class Practice2 {
static <N> void example(N[] numArr) {
for (N n : numArr) {
System.out.print(n + " ");
}
}
public static void main(String[] args) {
// int[] arr = { 1, 3, 5, 7, 9 }; 기본 데이터 타입 int는 제네릭을 사용할 수 없다.
Integer[] arr = { 1, 3, 5, 7, 9 };
example(arr);
}
}
메인 메서드에서 배열을 int로 선언해서 오류가 났다.
현재 위에 예제에서는 오토박싱, 오토언박싱이 이루어지고있다.
class IntroGenericArray
{
public static <T> void showArrayData(T[] arr)//제네릭 메소드임을 선언하기 위한 <T>
{
for(int i=0; i<arr.length; i++)
System.out.println(arr[i]);
}
public static void main(String[] args)
{
String[] stArr=new String[]{
"Hi!",
"I'm so happy",
"Java Generic Programming"
};
showArrayData(stArr);
}
}
public void hiMethod(Apple param) { ... }
답
public void onMethod(FruitBox param) { ... }
FruitBox 인스턴스의 참조 값이 전달 대상이 될 수 있다.
3-1. 만약 Apple클래스가 Fruit클래스를 상속하는 경우에 FruitBox\ 인스턴스의 참조 값이 위의 onMethod의 매개변수로 올 수 있을까? 중요
문법적으로 아니다. FruitBox\ 과 FruitBox\ 이 상속관계에 놓이는 것은 아니기 때문에 클래스가 정의되는 과정에서 키워드 extends 를 통해 상속됨이 명시되어야 한다.
FruitBox\ 인스턴스의 참조 값도 인자로 전달받을 수 있는 매개변수의 선언은 와일드 카드를 이용한 자료형의 명시를 허용한다.(상한 제한 와일드카드)
FruitBox<? extends Fruit> box1 = new FruitBox<Fruit>();
FruitBox<? extends Fruit> box2 = new FruitBox<Apple>();
가 의미하는 바는 "Fruit을 상속하는 모든 클래스" 이다. > ?
즉, 자료형을 결정 짓는 제네릭 매개변수 T에 Fruit클래스를 포함하여, Fruit을 상속하는 클래스라면 무엇이든 올 수 있음을 명시하는 것이다.
new FruitBox<'Fruit클래스 , 또는 Fruit을 상속하는 클래스의 이름'>() 의 형태로 생성되는 인스턴스면 무엇이든 참조 가능하다.FruitBox<? extends Fruit> box1 = new FruitBox<Fruit>();class FruitBox<? extends Fruit>{...}와일드카드를 이용해 특정 타입의 서브 클래스들을 나타내는 것이다.
'? extends Fruit'은 Fruit클래스를 상속하는 어떤 타입의 클래스나 인터페이스도 올 수 있음을 의미한다.
객체를 생성할 때 뿐 아니라 메소드에서도 사용 가능하다.
class Fruit
{
public void showYou()
{
System.out.println("난 과일입니다.");
}
}
class Apple extends Fruit
{
public void showYou()
{
super.showYou();
System.out.println("난 붉은 과일입니다.");
}
}
class FruitBox<T> //과일 객체를 저장하고, 필요할 때 꺼낼 수 있는 기능이 존재하는 클래스
{
T item;
public void store(T item) { this.item=item; }
public T pullOut() { return item; }
}
class IntroWildCard
{
public static void openAndShowFruitBox(FruitBox<? extends Fruit> box)//Fruit클래스나 그 하위 ㅋ르래스의 객체만 받을 수 있
{
Fruit fruit=box.pullOut(); //상위클래스의 참조변수로 하위클래스의 객체 주소값을 참조할 수 있으므로
fruit.showYou();
}
public static void main(String[] args)
{
FruitBox<Fruit> box1=new FruitBox<Fruit>(); //new FruitBox<>(); 로 생략가능
box1.store(new Fruit());
FruitBox<Apple> box2=new FruitBox<Apple>();
box2.store(new Apple());//Apple이 Fruit을 상속받고 있으므로 가능하다.
openAndShowFruitBox(box1);
openAndShowFruitBox(box2);
}
}
FruitBox<?> box; > Object에게 상속받는 모든 클래스
FruitBox<? extends Object> box; > 위와 같은 의미
FruitBox<? super Apple> boundedBox; > Apple이 상속하는 모든 클래스를 의미
extends는 "~을 상속하는 클래스라면 무엇이든지" > 해당 하위객체만 받을 수 o
super는 "~이 상속하는 클래스라면 무엇이든지" > 해당 상위 객체만 받을 수 O
class Fruit
{
public String toString()
{
return "과일";
}
}
class Apple extends Fruit
{
public String toString()
{
return "사과";
}
}
class RedApple extends Apple
{
public String toString()
{
return "빨간 사과";
}
}
class FruitBox<T>
{
T item;
public void store(T item)
{
this.item = item;
}
public T pullOut()
{
return item;
}
}
public class Test {
public static void show(FruitBox<? super Apple> box)
{
System.out.println(box.pullOut());
}
public static void main(String[] args) {
FruitBox<Apple> fbox1 = new FruitBox<Apple>();
fbox1.store(new Apple());
show(fbox1);
FruitBox<Fruit> fbox2 = new FruitBox<Fruit>();
fbox2.store(new Fruit());
show(fbox2);
FruitBox<RedApple> fbox3 = new FruitBox<RedApple>();
fbox3.store(new RedApple());
// show(fbox3); // 에러
}
}
show메서드의 인자값으로는 Apple, Apple의 상위클래스 Fruit, Object 자료형이 올 수 있다.
주석처리한 부분(에러): show메서드의 인자는 Apple과 Fruit, Object를 저장하는 상자만 들어갈 수 있고, 외에 하위 클래스는 올 수 없다. >>Apple을 포함한 Apple의 상위클래스만 올 수 있다.
class AAA<T>
{
T itemAAA;
}
class BBB<T> extends AAA<T>
{
T itemBBB;
}
이렇게 상속이 되면, 하나의 자료형 정보로 인해서 AAA의 자료형과 BBB의 자료형이 모두 결정된다.
즉, 다음과 같이 문장을 구성하면, T가 각각 String과 Integer로 대체되어 인스턴스가 생성된다.(Wrapper Class 로 활용 가능)
BBB<String> myString = new BBB<String>();
BBB<Integer> myInteger = new BBB<Integer>();
class BBB extends AAA<String>
{
int itemBBB;
}
위의 BBB 클래스는 제네릭으로 정의될 수도 있다.
그러나 제네릭이 아니어도 된다는 것을 강조하기 위해서 일반 클래스로 정의하였다.
interface MyInterface<T>
{
public T myFunc(T item);
}
interface AAA<T>
{
void aaa(T item);
}
인터페이스를 구현하여 클래스를 정의하는 방식 두가지
class MyImplement<T> implements MyInterface<T>
{
public T myFunc(T item)//인터페이스 구현
{
return item;
}
}
class MyImplement implements MyInterface<String>
{
public String myFunc(String item)//제네릭을 String 으로 지정해 구현도 가능
{
return item;
}
}
주의할점
위의 클래스 정의와 같이 T의 자료형이 String으로 결정되면, MyInterface의 메소드 myFunc를 구현할 때에도 T가 아닌 String으로 명시해야 한다.
제네릭 타입의 유연성을 높여주는 기능이다.
제네릭 타입을 사용할 때, 타입 매개변수를 명확히 지정하지 않고 일부 불확실한 경우에 사용된다.
와일드 카드는 물음표 기호(?)로 표시되며, 크게 세 가지 유형이 있다.
<?>: 비한정적 와일드카드(Unbounded Wildcards)
<? extends T>: 상한 와일드카드(Upper Bounded Wildcards)
<? super T>: 하한 와일드카드(Lower Bounded Wildcards)
하한 와일드카드는 특정 타입의 상위 타입만을 허용한다.
특정 타입과 그 타입의 상위 타입들만을 처리할 수 있다.
와일드 카드는 제네릭 메서드, 제네릭 클래스, 제네릭 인터페이스에서 사용될 수 있으며, 코드의 유연성을 높이고 재사용성을 향상시킨다.
예제1
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoxInBox {
public static void main(String[] args) {
Box<String> sBox = new Box<>(); //<>안에 생략 가능
sBox.set("I am so happy.");
Box<Box<String>> wBox = new Box<>();
wBox.set(sBox);
Box<Box<Box<String>>> zBox = new Box<>();
zBox.set(wBox);
System.out.println(zBox.get().get().get()); //wBox >> sBox >> 문자열을 꺼내게되는
}
}
타입 인자의 생략 : 다이아몬드(Diamond) 기호
Box<Apple> aBox = new Box<>();형태로 객체 생성 가능
위처럼 중첩해서 객체를 생성했다면 출력해서 꺼낼때, 꺼내고 싶은 객체의 중첩양 만큼 get()메소드도 같은 양으로 꺼내야한다.
위에 객체 생성할때 new Box<>(); 하면 자바 자동완성에서는 그냥 new Box();로 자동완성 하는데, 기본적으로 같은 동작을 수행하지만, 자바가 자동완성하는 new Box();는 제네릭 타입의 매개변수를 생략하는 것이기 때문에 경고를 발생한다.
class Box<T extends Number> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoundedBox {
public static void main(String[] args) {
Box<Integer> iBox = new Box<>(); // Integer는 Number를 상속 > 객체 형태
iBox.set(24);
Box<Double> dBox = new Box<>(); // Double은 Number를 상속
dBox.set(5.97); //오토박싱 >> double객체의 주소값이 들어가있는것
System.out.println(iBox.get());
System.out.println(dBox.get());
}
}
위처럼 Number클래스를 상속하는 클래스의 인스턴스만 담고 싶다면
class Box<T extends Number> {...} > 인스턴스 생성 시 타입 인자로 Number또는 이를 상속하는 클래스만 올 수 있게 된다.class Box<T>{
private T ob;
...
public int toIntValue() {
return ob.intValue(); // Error!
}
}
모든 클래스가 intValue()메소드가 있는지 없는지 모르기 때문에 위와같은 코드는 Object메소드만 호출할 수 있다.
class Box<T extends Number> {
private T ob;
...
public int toIntValue() {
return ob.intValue(); // OK!
}
}
interface Eatable {
public String eat();
}
class Apple implements Eatable {
public String toString() {
return "I am an apple.";
}
@Override
public String eat() {
return "It tastes so good!";
}
}
class Box<T extends Eatable> {//Eatable을 구현하는 클래스가 올 수 있게끔 제약을 두었다.
private T ob;
public void set(T o) {
ob = o;
}
// 한 입 먹고 반환하는 행위의 메소드로 수정
public T get() {
System.out.println(ob.eat()); // Eatable로 제한하였기에 eat 호출 가능
return ob;
}
}
class BoundedInterfaceBox {
public static void main(String[] args) {
Box<Apple> box = new Box<>();
box.set(new Apple()); // 사과 저장
Apple ap = box.get(); // 사과 꺼내기
System.out.println(ap);
}
}
Box클래스의 타입 인자에서 Eatable인터페이스를 구현하는 클래스로 타입 인자를 제한했기 때문에 인터페이스에 선언되어 있는 eat()메소드의 호출이 가능한것이다.
타입 인자를 제한할 때는 하나의 클래스와 하나 이상의 인터페이스에 대해 동시에 제한을 할 수 있다.
class Box<T extends Number & Eatable> {...} >> Number를 상속하고 Eatable인터페이스를 구현하는 클래스만이 올 수 있도록 제한한것이다. public static <T> Box<T> makeBox(T o) {
메소드의 이름: makeBox
반환형: Box
제네릭 타입
static과 Box사이에 위치한 가 T타입 매개변수임을 알리는 표시다.
위와 같은 메소드 정의를 보면 Box가 반환형임을, 그리고 그 앞에 위치한 는 T가 타입 매개변수임을 알리는 표시임을 알 수 있어야 한다.(반환타입 이전이 타입 명시)
<> public static Box<T> makeBox(T o) {...} 이렇게 표시하면 에러남
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoxFactory {
public static <T> Box<T> makeBox(T o) { // 제네릭 메소드의 정의
Box<T> box = new Box<T>(); // 상자를 생성하고,
box.set(o); // 전달된 인스턴스를 상자에 담아서,
return box; // 이 상자를 반환한다.
}
}
class GenericMethodBoxMaker {
public static void main(String[] args) {
Box<String> sBox = BoxFactory.makeBox("Sweet");
System.out.println(sBox.get());
Box<Double> dBox = BoxFactory.makeBox(7.59);
System.out.println(dBox.get());
}
}
예제1
interface Getable<T> {
public T get();
}
class Box<T> implements Getable<T> { // Box<T> 클래스는 다음과 같이 Getable<T> 인터페이스를 구현하는 형태로 정의
private T ob;
public void set(T o) { ob = o; }
@Override
public T get() {
return ob;
}
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class GetableGenericInterface {
public static void main(String[] args) {
Box<Toy> box = new Box<>();
box.set(new Toy());
Getable<Toy> gt = box;
System.out.println(gt.get());
}
}
class Box<T> implements Getable<String> {...}예제 2 - 인터페이스에서 제네릭을 지정했을 때
interface Getable<T> {
public T get();
}
class Box<T> implements Getable<String> {
private T ob;
public void set(T o) { ob = o; }
@Override
public String get() { //제네릭을 String으로 두었으니 여기서도 String 형으로
return ob.toString();
}
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class GetableGenericInterface2 {
public static void main(String[] args) {
Box<Toy> box = new Box<>();
box.set(new Toy());
Getable<String> gt = box;
System.out.println(gt.get());
}
}
Getable형 참조변수는 위 메인 메서드에서와 같이 Box인스턴스를 T의 자료형에 상관없이 참조할 수 있다.
- 만약 Box 클래스가 Getable 인터페이스를 직접 구현하는 것이 아니라, 다른 클래스가 Getable 인터페이스를 구현한 객체를 반환하는 메서드를 가지고 있을 때
Box<String> sBox = BoxFactory.<String>makeBox("Sweet");
Box<Double> dBox = BoxFactory.<Double>makeBox(7.59); // 7.59에 대해 오토 박싱 진행됨
Box<String> sBox = BoxFactory.makeBox("Sweet");
Box<Double> dBox = BoxFactory.makeBox(7.59); // 7.59에 대해 오토 박싱 진행됨
class Box<T> {
protected T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class SteelBox<T> extends Box<T> {//제네릭 클래스의 상속
public SteelBox(T o) { // 제네릭 클래스의 생성자
ob = o;
}//이 클래스로 인해 메인 메서드에서와 같이 Box<T>의 참조변수로 SteelBox<T>인스턴스를 참조하는 문장을 구성할 수있
}
class GenericInheritance {
public static void main(String[] args) {
Box<Integer> iBox = new SteelBox<>(7959);
Box<String> sBox = new SteelBox<>("Simple");
System.out.println(iBox.get());
System.out.println(sBox.get());
}
}
'SteelBox 클래스는 Box 클래스를 상속한다."
"SteelBox 제네릭 타입은 Box 지네릭 타입을 상속한다." 라고 표현한다.
Box<Number> box = new Box<Integer>(); >> 컴파일 불가능
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
@Override
public String toString() {
return ob.toString();
}
}
class Unboxer {
public static <T> T openBox(Box<T> box) {
return box.get();
}
public static void peekBox(Box<?> box) { // 와일드카드 사용
System.out.println(box);
}
}
class WildcardUnboxer2 {
public static void main(String[] args) {
Box<String> box = new Box<>();
box.set("So Simple String");
Unboxer.peekBox(box);
}
}
public static void peekBox(Box box){
System.out.println(box);}
public static void peekBox(Box<?> box) {
System.out.println(box);}
기능적인 측면에서 보면 위의 두 메소드는 완전히 동일하다. 즉 제네릭 메소드와 와일드카드 기반 메소드는 상호 대체 가능한 측면이 있다.
그러나 코드가 조금 더 간결하다는 이유로 와일드카드 기반 메소드의 정의를 선호한다.
하한 제한된 와일드카드로 선언하는 제네릭 메소드
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
@Override
public String toString() {
return ob.toString();
}
}
class Unboxer {
public static void peekBox(Box<? super Integer> box) {
System.out.println(box);
}//Box<Integer>, Box<Number>, Box<Object> 의 타입종류만 올 수 있
}
class LowerBoundedWildcard {
public static void main(String[] args) {
Box<Integer> iBox = new Box<Integer>();
iBox.set(5577);
Box<Number> nBox = new Box<Number>();
// nBox.set(new Integer(9955)); // deprecated API.
nBox.set(Integer.valueOf(9955));
Box<Object> oBox = new Box<Object>();
oBox.set("My Simple Instance");
Unboxer.peekBox(iBox);
Unboxer.peekBox(nBox);
Unboxer.peekBox(oBox);
}
}
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class Toy { //장난감을 나타내는 클래
@Override
public String toString() {
return "I am a Toy";
}
}
class BoxHandler {
public static void outBox(Box<Toy> box) {
Toy t = box.get(); // 박스에서 꺼내기
System.out.println(t);
}
public static void inBox(Box<Toy> box, Toy n) {
box.set(n); // 박스에 넣기
}
}
class BoundedWildcardBase {
public static void main(String[] args) {
Box<Toy> box = new Box<>();
BoxHandler.inBox(box, new Toy());
BoxHandler.outBox(box);
}
}
와일드카드를 사용하는 이유는 메서드가 호출될 때 컴파일러가 유형 안전성을 보장하기 위함
outBox() 메서드에 Box<Toy> 인스턴스를 전달하면 Toy 클래스의 하위 클래스가 아닌 다른 타입의 인스턴스를 저장하려고 할 때 컴파일 오류가 발생합니다.
이를 통해 메서드의 호출자가 오류를 컴파일 시간에 발견할 수 있습니다.
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class BoxContentsMover {
// from에 저장된 내용물을 to로 이동
public static void moveBox(Box<? super Toy> to, Box<? extends Toy> from) {
to.set(from.get());
}
}
class MoveBoxContents {
public static void main(String[] args) {
Box<Toy> box1 = new Box<>();
box1.set(new Toy());
// box1에 저장된 내용물 box2로 이동
Box<Toy> box2 = new Box<>();
BoxContentsMover.moveBox(box2, box1);
System.out.println(box2.get());
}
}
이같이 하는 이유는 실수가 컴파일 과정에서 드러나기 위해 제한을 걸어놓는 것이다.
매개변수 선언: Box<? extends Toy> box
box가 참조하는 인스턴스를 대상으로 꺼내는 작업만 허용
매개변수 선언: Box<? super Toy> box
box가 참조하는 인스턴스를 대상으로 넣는 작업만 허용
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class Robot {
@Override
public String toString() {
return "I am a Robot";
}
}
class BoxHandler {
public static <T> void outBox(Box<? extends T> box) { //꺼내기만 할 수 있는 메소드
T t = box.get();
System.out.println(t);
}
public static <T> void inBox(Box<? super T> box, T n) {//넣기만 할 수 있는 메소
box.set(n);
}
}//Box<Toy> 인스턴스와 Box<Robot> 인스턴스를 동시에 허용할 수 있도록 정
class BoundedWildcardGenericMethod {
public static void main(String[] args) {
Box<Toy> tBox = new Box<>();
BoxHandler.inBox(tBox, new Toy());
BoxHandler.outBox(tBox);
Box<Robot> rBox = new Box<>();
BoxHandler.inBox(rBox, new Robot());
BoxHandler.outBox(rBox);
}
}
아래와 같이 오버로딩하여 메소드를 정의하는 방법이 존재한다.
class BoxHandler {
// 다음 두 메소드는 오버로딩 인정 안됨.
public static void outBox(Box<? extends Toy> box) {...}
public static void outBox(Box<? extends Robot> box) {...}
// 다음 두 메소드는 두 번째 매개변수로 인해 오버로딩 인정 됨.
public static void inBox(Box<? super Toy> box, Toy n) {...}
public static void inBox(Box<? super Robot> box, Robot n) {...}
}
첫번째 / 두 개의 메소드의 오버로딩이 성립하지 않는 이유는
바는 제네릭 등장 이전에 정의된 클래스들과의 상호 호환성 유지를 위해 컴파일 시 제네릭과 와일드카드 관련 정보를 지우는 과정을 거친다. 즉 위의 두 매개변수 선언은 컴파일 과정에서 다음과 같이 수정이 된다.
위와 같이 컴파일러가 제네릭 정보를 지우는 행위를 가리켜 'Type Erasure'라 한다. 아래와 같이 에러 발생
name clash:
outBox(Box<? extends Robot>) and outBox(Box<? extends Toy>)have the same erasure
==
{이름 충돌 : outBox(Box<? extends Robot>)와 outBox(Box<? extends Toy>)은 'Type Erasure'에 의해 매개변수 정보가 같아집니다. }
두번째 / 오버로딩이 인정되는 이유는 제네릭과 관련없는 두 번째 매개변수의 자료형이 다르기 때문이다.
public static <T> void outBox(Box<? extends T> box) {...}와 같은 제네릭 메소드의 정의로 대신한다.
c:\JavaStudy>java DDBoxDemo
Apple & 25
Orange & 33
내가 푼것
package com.test.memo;
class DBox<L, R> {
private L left;
private R right;
public void set(L o, R r) {
left = o;
right = r;
}
public String toString() {
return left + " & " + right;
}
}
class DDBox<U, D> {
private U fruit;
private D num;
void set(U f, D n) {
fruit = f;
num = n;
}
@Override
public String toString() {
return super.toString() + "\n" + fruit + "\n" + num;
}
}
public class Practice2 {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
ddbox.set(box1, box2); // 두 개의 상자를 하나의 상자에 담음
System.out.println(ddbox); // 상자의 내용물 출력
}
}

선생님 풀이
class DBox<L, R> {
private L left; // 왼쪽 수납 공간
private R right; // 오른쪽 수납 공간
public void set(L o, R r) {
left = o;
right = r;
}
@Override
public String toString() {
return left + " & " +right;
}
}
class DDBox<U, D> {
private U up;
private D down;
public void set(U u, D d) {
up = u;
down = d;
}
@Override
public String toString() {
return up.toString() + "\n" + down.toString();
}
}
class DDBoxDemo {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
ddbox.set(box1, box2);
System.out.println(ddbox);
}
}
선생님 풀이
package com.test.memo;
class DBox<L, R> {
private L left;
private R right;
public void set(L o, R r) {
left = o;
right = r;
}
public String toString() {
return left + " & " + right;
}
}
//class DDBox<U, D> {
// private U fruit;
// private D num;
//
// void set(U f, D n) {
// fruit = f;
// num = n;
// }
//
// @Override
// public String toString() {
// return super.toString() + "\n" + fruit + "\n" + num;
// }
//}
public class Practice2 {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DBox<DBox<String, Integer>, DBox<String, Integer>> ddBox = new DBox<>();
ddBox.set(box1, box2);
System.out.println(ddBox);
// DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
// ddbox.set(box1, box2); // 두 개의 상자를 하나의 상자에 담음
// System.out.println(ddbox); // 상자의 내용물 출력
}
}
class Box {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoxSwapDemo {
// 이 위치에 swapBox 메소드 정의하자.
public static void main(String[] args) {
Box box1 = new Box<>();
box1.set(99);
Box box2 = new Box<>();
box2.set(55);
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2); // 정의해야 할 swapBox 메소드
System.out.println(box1.get() + " & " + box2.get());
}
}
99 & 55
55 & 99
package com.test.memo;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class Practice2 {
private static <T extends Number> void swapBox(Box<T> box1, Box<T> box2) {
T temp = box1.get();
box1.set(box2.get());
box2.set(temp);
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(99);
Box<Integer> box2 = new Box<>();
box2.set(55);
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2); // 정의해야 할 swapBox 메소드
System.out.println(box1.get() + " & " + box2.get());
}
}
Number를 상속하게 제한을 둔다.
객체로 저장되어 있으니, get과 set을 이용해 치환해주는 것이다.
컴파일 과정에서는 이 실수가 드러나지 않는다. 실수가 컴파일 과정에서 발견될 수 있도록 매개변수 선언을 수정하자. 그리고 프로그래머의 실수를 바로잡자.
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class BoundedWildcardDemo {
public static void addBox(Box<Integer> b1, Box<Integer> b2, Box<Integer> b3) {
b3.set(b1.get() + b2.get()); // 프로그래머의 실수가 있는 부분
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<Integer> box2 = new Box<>();
box2.set(37);
Box<Integer> result = new Box<>();
result.set(0);
addBox(result, box1, box2); // result에 24 + 37의 결과 저장
System.out.println(result.get()); // 61 출력
}
}
메인 메서드에서 값을 저장할 result가 b1에 들어가는데 갑자기 b3에 값을 담는거 아닌가..?
정답
package com.test.memo;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class Practice2 { //꺼내는게 안되고 넣는게 안된다.
public static void addBox(Box<? super Integer> b1, Box<? extends Integer> b2, Box<? extends Integer> b3) {
b1.set(b2.get() + b3.get()); // 프로그래머의 실수가 있던 부분
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<Integer> box2 = new Box<>();
box2.set(37);
Box<Integer> result = new Box<>();
result.set(0);
addBox(result, box1, box2); // result에 24 + 37의 결과 저장
System.out.println(result.get()); // 61 출력
}
}
메서드 인자값에 와일드카드를 다 넣어서 넣기만 할 수 있고, 꺼낼기만 할 수 있게 고치면 우리가 눈에 알 수 있게 컴파일 에러가 난다. >> 개발자가 실수한것을 인지할 수 있도록
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class BoundedWildcardGeneric {
// box에 con과 동일한 내용물이 들었는지 확인
public static <T> boolean compBox(Box<T> box, T con) {
T bc = box.get();
box.set(con); // 프로그래머의 실수로 삽입된 문장, 때문에 내용물이 바뀐다.
return bc.equals(con);
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<String> box2 = new Box<>();
box2.set("Poly");
if(compBox(box1, 25))
System.out.println("상자 안에 25 저장");
if(compBox(box2, "Moly"))
System.out.println("상자 안에 Moly 저장");
System.out.println(box1.get());
System.out.println(box2.get());
}
}
set.() 하는 메서드의 인자값인 box<? extends T> 로 변경시키면 컴파일 할 때 오류가 뜨는것을 발견할 수 있다.
이클립스 소스코드 비교하는 법