문제

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

메소드에 대한 호출은 유형-점검되지 않습니다. 다음과 같이 매개 변수를 캐스팅 할 수 없습니다.

wahey((List<Object>) new LinkedList<Number>());

내 연구에서, 나는 이것을 허용하지 않는 이유가 유형 안전이라고 모았다. 위의 일을 허용했다면 다음을 가질 수 있습니다.

List<Double> ld;
wahey(ld);

메소드 wahey 내부에서는 입력 목록에 문자열을 추가 할 수 있습니다 (매개 변수가 List<Object> 참조). 이제 메소드 호출 후 LD는 유형의 목록을 나타냅니다. List<Double>, 그러나 실제 목록에는 일부 문자열 객체가 포함되어 있습니다!

이것은 Java가 제네릭없이 작동하는 일반적인 방식과 다르게 보입니다. 예를 들어:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

우리가 여기서하고있는 일은 본질적으로 동일한 일입니다. 이것은 컴파일 타임 검사를 통과하고 런타임에만 실패한다는 점을 제외하고는 본질적으로 동일합니다. 목록이있는 버전은 컴파일되지 않습니다.

이것은 이것이 제네릭의 유형 제한과 관련하여 순전히 설계 결정이라고 믿게합니다. 이 결정에 대한 의견을 얻고 싶었습니까?

도움이 되었습니까?

해결책

당신이 "제네릭이없는"예제에서하고있는 일은 캐스트이며, 이는 당신이 무언가를 유형 미공일을하고 있음을 분명히합니다. 제네릭과 동등한 것은 다음과 같습니다.

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

동일한 방식으로 동작합니다 (컴파일하지만 런타임에 실패). 당신이 요구하는 행동을 허용하지 않는 이유는 그것이 허용하기 때문입니다. 절대적인 코드에서 눈치 채기가 훨씬 어려운 유형-금지 액션. 예를 들어:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

이것은 당신이 다음과 같이 부를 때까지 완벽하게 미세하고 유형-안전하게 보입니다.

List<Double> list_d;
String s;

Foo(list_d, s);

발신자로서 FOO가 매개 변수로 무엇을할지 알지 못하기 때문에 유형-안전하게 보입니다.

따라서이 경우 두 개의 겉보기에 안전한 코드 비트가 있으며, 결국 유형은 유형이 없습니다. 숨겨져 있고 피하기가 어렵고 디버깅하기가 어렵 기 때문에 나쁘다.

다른 팁

그것이 ...인지 고려하십시오 ...

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

상기 예에서 알 수 있듯이 위의 예에서 알 수 있듯이 모든 종류의 문제를 일으키는 부모 유형을 목록에 추가 할 수 있습니다. 따라서 허용되지 않습니다.

제네릭의 작동 방식으로 인해 서로의 하위 유형이 아닙니다. 당신이 원하는 것은 다음과 같이 기능을 선언하는 것입니다.

public void wahey(List<?> list) {}

그런 다음 객체를 확장하는 모든 것의 목록을 수락합니다. 당신은 또한 할 수 있습니다 :

public void wahey(List<? extends Number> list) {}

이것은 당신이 숫자의 서브 클래스 인 목록을 가져갈 수있게 해줄 것입니다.

Maurice Naftalin & Philip Wadler의 "Java Generics and Collections"사본을 선택하는 것이 좋습니다.

여기에는 본질적으로 두 가지 차원의 추상화, 목록 추상화 및 내용의 추상화가 있습니다. 예를 들어, 링크드 목록이거나 Arraylist라는 점에서 목록 추상화를 따라 다양하게는 완벽하게 괜찮습니다. 그러나 내용을 더욱 제한하는 것은 좋지 않습니다. 숫자 만 보유합니다). (개체를 보유하는 목록)로 알고있는 모든 참조는 유형의 계약에 따라 보유 할 수 있음을 이해하기 때문입니다. 어느 물체.

이것은 당신이 말한 비 전기 예제 코드에서 수행 한 것과는 상당히 다릅니다. 대신 당신은 다음과 같이 말하려고합니다. 그리고 그것은 그렇지 않으며 컴파일러는 그것을 감지 할 수 있으므로 그것을 벗어날 수 없습니다.

"우리가 여기서하고있는 일은 본질적으로 동일한 일입니다. 이것을 제외하고는 컴파일 타임 검사를 통과하고 런타임에만 실패합니다. 목록이있는 버전은 컴파일되지 않습니다."

Java Generics의 주요 목적은 실행 시간 대신 컴파일 시간에 유형 비 호환성을 실패시키는 것임을 고려할 때 관찰하는 것은 완벽하게 이해됩니다.

에서 java.sun.com

제네릭은 컬렉션의 유형을 컴파일러에 전달하여 확인할 수있는 방법을 제공합니다. 컴파일러가 컬렉션의 요소 유형을 알면 컴파일러는 컬렉션을 일관되게 사용했는지 확인하고 컬렉션에서 가져 오는 값에 올바른 캐스트를 삽입 할 수 있습니다.

자바에서 List<S> 아니다 하위 유형 List<T> 언제 S 하위 유형입니다 T. 이 규칙은 유형 안전을 제공합니다.

우리가 허용한다고 가정 해 봅시다 List<String> 하위 유형이되기 위해 List<Object>. 다음 예를 고려하십시오.

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

따라서,이를 강요하면 유형의 안전성을 제공하지만 단점도 있습니다. 덜 유연합니다. 다음 예를 고려하십시오.

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

여기에 당신은 모음이 있습니다 T다른 컬렉션의 모든 항목을 추가하려고합니다. 이 메소드를 a로 호출 할 수 없습니다 Collection<S> 용을 위해 S 하위 유형 T. 이상적으로는 컬렉션에 요소를 추가하기 때문에 파라미터 수집을 수정하지 않기 때문에 괜찮습니다.

이 문제를 해결하기 위해 Java는 "와일드 카드"라고 부르는 것을 제공합니다. 와일드 카드는 공분산/불균형을 제공하는 방법입니다. 이제 와일드 카드를 사용하여 다음을 고려하십시오.

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

이제 와일드 카드를 사용하여 T 형에서 공분산을 허용하고 타입 안전하지 않은 작업을 차단합니다 (예 : 컬렉션에 항목을 추가). 이렇게하면 유연성과 유형 안전을 얻습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top