Programing

제한된 와일드 카드 제네릭에 여러 인터페이스를 사용할 수없는 이유는 무엇입니까?

lottogame 2020. 11. 9. 07:38
반응형

제한된 와일드 카드 제네릭에 여러 인터페이스를 사용할 수없는 이유는 무엇입니까?


Java의 제네릭 유형에는 모든 종류의 직관에 반하는 속성이 있다는 것을 알고 있습니다. 특히 내가 이해하지 못하는 부분이 있는데, 누군가가 나에게 설명해 줄 수 있기를 바랍니다. 클래스 또는 인터페이스에 대한 형식 매개 변수를 지정할 때 .NET Framework를 사용하여 여러 인터페이스를 구현하도록 바인딩 할 수 있습니다 public class Foo<T extends InterfaceA & InterfaceB>. 그러나 실제 개체를 인스턴스화하는 경우 더 이상 작동하지 않습니다. List<? extends InterfaceA>괜찮지 만 List<? extends InterfaceA & InterfaceB>컴파일에 실패했습니다. 다음 전체 스 니펫을 고려하십시오.

import java.util.List;

public class Test {

  static interface A {
    public int getSomething();
  }

  static interface B {
    public int getSomethingElse();
  }

  static class AandB implements A, B {
    public int getSomething() { return 1; }
    public int getSomethingElse() { return 2; }
  }

  // Notice the multiple bounds here. This works.
  static class AandBList<T extends A & B> {
    List<T> list;

    public List<T> getList() { return list; }
  }

  public static void main(String [] args) {
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    // This last one fails to compile!
    List<? extends A & B> foobar = new LinkedList<AandB>();
  }
}

의 의미 bar가 잘 정의되어야 하는 것 같습니다 . 하나가 아닌 두 유형의 교차를 허용함으로써 유형 안전성의 손실을 생각할 수 없습니다. 그래도 설명이 있다고 확신합니다. 누구든지 그것이 무엇인지 알고 있습니까?


흥미롭게도 인터페이스 java.lang.reflect.WildcardType는 와일드 카드 인수에 대해 상한과 하한을 모두 지원하는 것처럼 보입니다. 각각은 여러 경계를 포함 할 수 있습니다.

Type[] getUpperBounds();
Type[] getLowerBounds();

이것은 언어가 허용하는 것 이상의 방법입니다. 소스 코드에 숨겨진 주석이 있습니다.

// one or many? Up to language spec; currently only one, but this API
// allows for generalization.

인터페이스 작성자는 이것이 우연한 제한이라고 생각하는 것 같습니다.

귀하의 질문에 대한 정답은 제네릭이 이미 너무 복잡하다는 것입니다. 더 많은 복잡성을 추가하는 것이 마지막 빨대가 될 수 있습니다.

와일드 카드가 여러 상한을 가질 수 있도록하려면 사양을 스캔하고 전체 시스템이 계속 작동하는지 확인해야합니다.

내가 아는 한 가지 문제는 유형 추론에 있습니다. 현재의 추론 규칙은 단순히 intercection 유형을 다룰 수 없습니다. 제약을 줄이는 규칙은 없습니다 A&B << C. 우리가 그것을 줄이면

    A<<C 
  or
    A<<B

현재의 모든 추론 엔진은 이러한 분기를 허용하기 위해 대대적 인 점검을 거쳐야합니다. 그러나 진짜 심각한 문제는 이것이 여러 가지 해결책을 허용하지만 서로를 선호 할 이유가 없다는 것입니다.

그러나 추론은 형식 안전성에 필수적인 것은 아닙니다. 이 경우 추론을 거부하고 프로그래머에게 유형 인수를 명시 적으로 채우도록 요청할 수 있습니다. 따라서 추론의 어려움은 간섭 유형에 대한 강력한 주장이 아닙니다.


로부터 Java 언어 사양 :

4.9 교차 유형 교차 유형은 T1 & ... & Tn, n> 0 형식을 취합니다. 여기서 Ti, 1in은 유형 표현식입니다. 교차 유형은 캡처 변환 (§5.1.10) 및 유형 추론 (§15.12.2.7) 프로세스에서 발생합니다. 프로그램의 일부로 교차 유형을 직접 작성할 수 없습니다. 이를 지원하는 구문은 없습니다 . 교차 유형의 값은 1 인치에 대한 모든 유형 Ti의 값인 객체입니다.

그렇다면 이것이 지원되지 않는 이유는 무엇입니까? 내 생각 엔 그런 일로 무엇을해야합니까? -가능하다고 가정 해 봅시다 :

List<? extends A & B> list = ...

그럼 무엇을

list.get(0);

반환? 의 반환 값을 캡처하는 구문이 없습니다 A & B. 그런 목록에 무언가를 추가하는 것도 불가능하므로 기본적으로 쓸모가 없습니다.


No problem... just declare the type you need in the method signature.

This compiles:

public static <T extends A & B> void main(String[] args) throws Exception
{
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    List<T> foobar = new LinkedList<T>(); // This compiles!
}

Good question. It took me a while to figure out.

Lets simplify your case: You are trying to do the same as if you declare a class that extends 2 interfaces, and then a variable that has as a type those 2 interfaces, something like this:

  class MyClass implements Int1, Int2 { }

  Int1 & Int2 variable = new MyClass()

Of course, illegal. And this is equivalent to what you try to do with generics. What you are trying to do is:

  List<? extends A & B> foobar;

But then, to use foobar, you would need to use a variable of both interfaces this way:

  A & B element = foobar.get(0);

Which is not legal in Java. This means, you are declaring the elements of the list as beeing of 2 types simultaneously, and even if our brains can deal with it, Java language cannot.


For what it's worth: if anyone's wondering this because they would truly like to use this in practice, I've worked around it by defining an interface that contains the union of all methods in all the interfaces and class that I'm working with. i.e. I was trying to do the following:

class A {}

interface B {}

List<? extends A & B> list;

which is illegal - so instead I did this:

class A {
  <A methods>
}

interface B {
  <B methods>
}

interface C {
  <A methods>
  <B methods>
}

List<C> list;

This still isn't as useful as being able to type something as List<? extends A implements B>, e.g. if someone adds or removes methods to A or B, the typing of the list will not be updated automatically, it requires a manual change to C. But it's worked for my needs.

참고URL : https://stackoverflow.com/questions/6643241/why-cant-you-have-multiple-interfaces-in-a-bounded-wildcard-generic

반응형