문제

일반 유형 매개 변수와 함께 인터페이스를 사용하고 있다고 가정합니다.

interface Foo<T> {
  T getOne();
  void useOne(T t);
}

의도는 그 유형입니다 T 초록 : 구현에 대한 유형 제약 조건을 시행합니다. Foo, 그러나 클라이언트 코드는 정확히 무엇을 신경 쓰지 않습니다 T 이다.

이것은 일반적인 방법의 맥락에서 문제가되지 않습니다.

public <T> void doStuff(Foo<T> foo) {
  T t = foo.getOne();
  /* do stuff */
  foo.useOne(t);
}

하지만 내가 일을 분해하고 싶다고 가정 해 doStuff, 수업에서 일부 상태를 저장합니다 Bar. 이 경우 Foo 에게 Bar.

public class Bar<T> {
  private Foo<T> foo;
  private T t;

  /* ... */

  public void startStuff() {
    t = foo.getOne();
  }

  public void finishStuff() {
    foo.useOne(t);
  }
}

유형 매개 변수이기 때문에 이것은 이상합니다. T 공개 인터페이스에 나타나지 않습니다 Bar (즉, 메소드 매개 변수 또는 반환 유형에 포함되지 않습니다). "정량화하는 방법이 있습니까? T 멀리 "? 즉, 매개 변수를 정리할 수 있습니까? T 인터페이스에 숨겨져 있습니다 Bar, 다음과 같이?

public class Bar {
  <T> { // foo and t have to use the same T
    private Foo<T> foo;
    private T t;
  } // T is out of scope
  ... 
}
도움이 되었습니까?

해결책

귀하의 문제는 a "캡처 도우미", 그러나 두 가지 별도의 방법이 사용되는 두 번째 예제에 적용될 수 있는지 확신 할 수 없습니다. 너의 첫번째 doStuff 방법은 분명히 더 잘 작성 될 수 있습니다 public void doStuff(Foo<?> foo), 그것은 관계없이 작동하기 때문입니다 Foo 유형 매개 변수. 그런 다음 "캡처 헬퍼"패턴이 유용합니다.


업데이트 : Goetz의 캡처 도우미 아이디어를 확장하여 조금 땜질을 한 후, 나는 이것을 생각해 냈습니다. 내부, 그것은 조금 지저분 해 보인다. 외부에서, 당신은 의심하지 않을 것입니다.

public class Bar {
  private final Helper<?> helper;
  public Bar(Foo<?> foo) {
    this.helper = Helper.create(foo);
  }
  public void startStuff() {
    helper.startStuff();
  }
  public void finishStuff() {
    helper.finishStuff();
  }
  private static class Helper<T> {
    private final Foo<T> foo;
    private T t;
    private Helper(Foo<T> foo) {
      this.foo = foo;
    }
    static <T> Helper<T> create(Foo<T> foo) {
      return new Helper<T>(foo);
    }
    void startStuff() {
      t = foo.getOne();
    }
    void finishStuff() {
      foo.useOne(t);
    }
  }
}

다른 팁

유용하기 위해, 어느 시점에서 당신은 foo 필드. 그 시점에서 당신은 알아야합니다 (또는 캡처 할 수 있음) T. 나는 그것을 생성자에서하는 것을 제안하고, 그것은 의미가 있습니다. Bar 일반 매개 변수를 갖습니다. 클라이언트 코드가 유형을 볼 필요가 없도록 인터페이스를 사용할 수도 있습니다. 그러나 나는 당신이 내 조언을받지 않을 것이라고 생각하고 정말로 setFoo. 따라서 전환 가능한 구현에 포인트를 추가하십시오.

/* pp */ class final BarImpl<T> {
    private final Foo<T> foo;
    private T t;

    BarImpl(Foo<T> foo) {
        this.foo = foo;
    }

    public void startStuff() {
        t = foo.getOne();
    }

    public void finishStuff() {
        foo.useOne(t);
    }
}

public final class Bar {
    private BarImpl<?> impl;

    /* ... */

    // Need to capture this wildcard, because constructors suck (pre-JDK7?).
    public void setFoo(Foo<?> foo) {
        setFooImpl(foo);
    }
    private <T> void setFooImpl(Foo<T> foo) {
        impl = new BarImpl<T>(foo);
    }

    public void startStuff() {
        impl.startStuff();
    }

    public void finishStuff() {
        impl.finishStuff();
    }
}

3 계층 계층 구조가없는 이유는 무엇입니까?

abstract class Foo

abstract class FooImplBase<T> extends Foo

class Bar extends FooImplBase<String>

고객 은만 알고 있습니다 Foo, 일반적인 방법이 포함되지 않습니다. 필요한 일반적인 방법을 소개하십시오 FooImplBase<T> 그리고 콘크리트 클래스는 그것에서 파생됩니다.

그래서 당신의 예에서 startStuff() 그리고 endStuff() 추상적입니다 Foo 그리고 구현되었습니다 FooImplBase<T>. 실제 상황에서 작동하는 것처럼 들리나요? 나는 그것이 약간 성가신 것에 동의합니다.

당신은 바 클래스를 정의하고 있습니다. 두 가지가 사실입니다 ...

1) 막대에 관련된 파라 메트릭 유형이 없습니다. 즉, FOO와 T 멤버는 정의를 위해 고정 된 단일 유형 (예 : u)을 가지고 있습니다. Foo에 할당 할 것을 기대하는 Foo를 통과 한 경우 Foo 여야합니다 u003CU>. 이것이 모두 사실이라면, 그렇습니다. 공개 인터페이스의 일부가 아닙니다. 모든 것이 특정 유형을 가지고 있습니다. 그런 다음 자유 유형 변수가 없기 때문에 "정량화"가 의미하는 바를 잘 모르겠습니다. 보편적으로 정량화된다는 의미라면 Bar가 파라 메트릭 타이핑이 없다는 사실을 어떻게 조정합니까? 따라서 각 구성원에게 구체적인 유형이 제공되어야합니까?u003C/u>

2) 막대에 관련된 파라 메트릭 유형이 있습니다. 분명히 공개 인터페이스에 있지는 않지만 아마도 당신은 foo를 통과 할 것입니다.u003CT> , 당신은 단일 유형 이상의 바 클래스를 인스턴스화하고 싶습니다. 그런 다음 언급 한 바와 같이, 이것은 공개 인터페이스에 있으며, 제네릭과 함께 t에서 막대를 매개 변수로 만들어야합니다. 이것은 "모든 유형 t에 대해이 정의는 참입니다"라는 정의에 대한 보편적 정량화를 제공합니다.

어딘가에 결정된 어딘가에, 바 클래스 내부의 't'에 어떤 유형을 사용할 것인지. 따라서 바의 정의 (FOO를 클래스 정의 내부에서 FOO로 교체)에서 선택해야하거나 막대 클래스의 클라이언트에 맡겨야합니다.이 경우에는 바가 일반화되어야합니다.

T에 의존하지 않는 인터페이스를 원한다면 그리고 t에 대해 다른 유형을 선택할 수 있으시면 다음과 같이 비 게 릭 인터페이스 또는 추상적 인 기본 클래스를 사용해야합니다.

interface Bar {
  void startStuff();
  // ...
}

class BarImplement<T> {
  private Foo<T> foo;
  // ...
}

정말로 약간의 IRE를 그리려면 Bar 클래스를 FOO 인터페이스 내부에 넣고 T를 빨아 들일 수 있습니다. 보다 이 기사 인터페이스 내부의 클래스에 대한 자세한 내용은 어쩌면 이것이 의미가있는 한 가지 사례일까요?

interface Foo<T> {
  T getOne();
  void useOne(T t);

  public class Bar<T> {
    private Foo<T> foo;
    private T t;
    public void startStuff() {
      t = foo.getOne();
    }
    public void finishStuff() {
      foo.useOne(t);
    }
  }
}

이 경우 매개 변수 T는 바에 관한 한, 컴파일 시간에 물체로 지워지 기 때문에 막대에 관한 한 쓸모가 없습니다. 그래서 당신은 또한 "문제를 구할 수있다"고 삭제를 일찍 할 수 있습니다.

public class Bar {

    private Foo<Object> foo;
    private Object t;

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