유형 추정 및 범용 방법 - [OOP 및 Java#7]

P, 본고는 다른 사람들이 제기한 재미있는 문제를 대상으로 쓴 것이다. 이 문제는 내가 범용과 어댑터에 대한 이해를 공고히 했다.
다음과 같이 가정합니다.
  • 항목 목록을 저장할 수 있는 Demo 클래스입니다.
  • 프로젝트는 클라이언트/호출자가 지정한 모든 유형일 수 있습니다.
  • Demo류에는 함수를 받아들여 원시 목록에 있는 새 항목 목록으로 되돌려주는 map 방법이 있습니다. 그러나 이 함수는 수정됩니다.
  • import java.util.List;
    import java.util.ArrayList;
    import java.util.function.Function;
    
    class Demo<T> {
      List<T> list;
      Demo(List<T> list) {
        this.list = list;
      }
    
      <U> Demo<U> map(Function<? super T, ? extends U> f) {
        List<U> answer = new ArrayList<U>();
        for (T item : this.list) {
          answer.add(f.apply(item));
        }
        return new Demo<U>(answer);
      }
    }
    
    추가 정보
  • <T> , <? super T, ? extends U>등.

  • 화목하다
  • Function<...>
  • 기능 인터페이스.함수를 Java의 첫 번째 유형으로 만들기 위한 단일 메소드 인터페이스라고 할 수 있습니다.여기서 예제 함수는 다음과 같이 설명할 수 있습니다.Function<Object, Integer> f = x -> x.hashCode(); f는 수신Object 유형의 입력과 출력Integer의 함수입니다.
  • 현재, Q1-10 중 어느 것이 오류 없이 번역될 수 있습니까?
    // turns an input object into its hash value representation
    Function<Object, Integer> f = x -> x.hashCode();
    
    // List of strings
    List<String> strings = new ArrayList<String>();
    strings.add("a");
    strings.add("b");
    
    // Q1 - 5
    Demo<Integer> list = new Demo<String>(strings).map(f);
    Demo<Number> list = new Demo<String>(strings).map(f);
    Demo<Object> list = new Demo<String>(strings).map(f);
    Demo<String> list = new Demo<String>(strings).map(f);
    Demo<> list = new Demo<String>(strings).map(f);
    
    // Q6 - 10
    (Demo<Integer>) new Demo<String>(strings).map(f);
    (Demo<Number>) new Demo<String>(strings).map(f);
    (Demo<Object>) new Demo<String>(strings).map(f);
    (Demo<String>) new Demo<String>(strings).map(f);
    (Demo) new Demo<String>(strings).map(f);
    

    답안
    1 좋아, 6 좋아.
    2, OK, 7 땡.
    3, OK, 8 오류.
    4 오류 9 오류
    5 땡, 10 오케이.

    분석


    관련된 유형 파라미터를 설명해 봅시다. 이것은 일을 분명히 할 것입니다.
    구조 함수를 사용하여 Demo 대상을 만들 때, 특정한 유형의 항목을 포함하는 목록을 전송해야 합니다.항목의 유형은 T 클래스의 유형Demo이 됩니다.그래서
    // note that map() method is not applied here
    // T is replaced by String
    Demo<String> listOfString = new Demo<String>(Arrays.asList("a", "b"));
    
    // T is replaced by Integer
    List<Integer> myList = new ArrayList<Integer>();
    myList.add(1);
    Demo<Integer> listOfInteger = new Demo<Integer>(myList));
    
    실제로 Demo 클래스는 다음과 같습니다.
    class Demo<String> {
      List<String> list;
      Demo(List<String> list) {
        this.list = list;
      }
    //...
    }
    
    class Demo<Integer> {
      List<Integer> list;
      Demo(List<Integer> list) {
        this.list = list;
      }
    //...
    }
    
    불변성 관계로 인해 다음과 같은 상황이 발생하지 않도록 주의하십시오.
    // ERROR
    Demo<Number> listOfNumber = new Demo<Integer>(Arrays.asList(1, 1));
    
    이제 질문 하나 볼게요. - 5.

    숙제에 관한 문제


    문장 1부터 5까지 다음을 시도합니다.
  • 호출Demo의 구조 함수 및 문자열 목록 전송
  • 새로 만든 실례에 .map(f) 방법을 호출하고 새로운 Demo 실례
  • 를 되돌려줍니다.
  • 실례를 성명된 변수에 부여합니다.

  • 건조사

    Demo<Integer> list = new Demo<String>(strings) .map(f);


    여기에는 문제가 없습니다. 왜냐하면 우리는 Demo 에서 TDemo 라고 성명했고 문자열 목록을 전달했기 때문입니다.구조 함수에 대해 말하자면, 우리는 정확한 유형의 매개 변수를 전달하고 있으며, 이것은 String의 실례를 되돌려 줄 것이다.
    Demo<String> 메서드

    Demo<Integer> list = new Demo<String>(strings) .map(f);


    여기가 헷갈리는 곳이야.중요한 관련 코드 세션을 나열합니다.
    Function<Object, Integer> f = x -> x.hashCode();
    
    class Demo<T> {
      List<T> list;
      Demo(List<T> list) {
        this.list = list;
      }
    
      <U> Demo<U> map(Function<? super T, ? extends U> f) {
        List<U> answer = new ArrayList<U>();
        for (T item : this.list) {
          answer.add(f.apply(item));
        }
        return new Demo<U>(answer);
      }
    }
    
    앞에서 구조 함수에 대한 토론에서 우리는 우리가 그 중에서 호출하는 방법map의 실례가 map인 것을 안다.
    따라서 호출Demo<String>map 클래스 중의 T 은 여전히 Demo 이다.따라서 String 클래스를 볼 수 있습니다. 여기서 Demo 클래스는 T 클래스로 바뀝니다.
    class Demo<String> {
      List<String> list;
      Demo(List<String> list) {
        this.list = list;
      }
    
      <U> Demo<U> map(Function<? super String, ? extends U> f) {
        List<U> answer = new ArrayList<U>();
        for (String item : this.list) {
          answer.add(f.apply(item));
        }
        return new Demo<U>(answer);
      }
    }
    
    현재, String 방법은 여전히 두 가지'미지수'가 있다.
  • map
  • ?
  • 우리가 U 방법을 호출할 때, 우리는 함수map를 전송하는데, 이 함수는 f 방법에'제공'유형을 주고, 이 방법이 map 매개 변수로 받아들이는 것에 동의해야 한다.
    우리 자세히 비교해 봅시다...
    Function<Object, Integer> f = x -> x.hashCode();
    <U> Demo<U> map(Function<? super String, ? extends U> f)  
    
    // for better visual, I will add spaces to align them
                    Function<     Object,       Integer>
    <U> Demo<U> map(Function<? super String, ? extends U>) 
    
    입력
    함수fObjectf의 첫 번째 매개 변수에 대응한다? super String.map뭘까요...
    • ? becomes Object.
    • Hence Object super String
    • Which is itself a valid statement as Object is indeed a parent of String

    <U> Demo<U> map(Function<Object super String, ? extends U>)
    
    출력
    함수?Integerf의 두 번째 매개 변수에 대응한다? extends U.map뭘까요...
    • ? becomes Integer, which is unsurprising
    • This is fine because ? is a wildcard and it can be of any type

    <U> Demo<U> map(Function<Object super String, Integer extends U>)
    
    ?뭘까요...
    명확한 설명이 없기 때문에U, 컴파일러는 추정할 것이다U.
    그것은 어떻게 추리를 진행합니까?
  • 반환된 객체를 선언된 변수에 할당하면 U 는 지정된 변수 유형이 됩니다.또는
  • 반환된 대상이 어떤 대상에게도 값을 부여하지 않으면 컴파일러는 방법 파라미터 외에 추정할 수 있는 다른 내용이 없다. 즉, U
  • 우리 배경에서.
  • ?는 LHS(Lature English Management)에서 지정한 유형이 됩니다.또는
  • UU의 유형이 됩니다.

  • 임무
    마지막으로 몇 가지 문제를 봅시다
    // Q1 - 5
    Demo<Integer> list = new Demo<String>(strings).map(f);
    Demo<Number> list = new Demo<String>(strings).map(f);
    Demo<Object> list = new Demo<String>(strings).map(f);
    Demo<String> list = new Demo<String>(strings).map(f);
    Demo<> list = new Demo<String>(strings).map(f);
    
    Q1-4에 대해 유형 추리를 통한 논리?는 각각 U, Integer, Number, Object로 바뀐다.
    Q2로만 설명
    Demo<Number> map(Function<Object super String, Integer extends Number> f) 
    
    String 자체가 유효한 문장입니다. Integer extends NumberNumber의 부급이기 때문입니다.
    따라서 출력에 주목하면 Q2의 방법 호출은 하나Integer로 되돌아갈 것이다. 이것은 Demo<Number>에 분배될 것이다. 아주 좋다!
    1분기와 3분기도 마찬가지다.
    왜 Q3가 틀렸어요?
    Demo<String> map(Function<Object super String, Integer extends String> f) 
    
    Demo<Number> listU 로 바꾸면서 우리는 이 모순을 얻었다.
  • String
  • Integer extends String의 상위 항목이 아니기 때문에 유효하지 않습니다.
  • 컴파일러가 제공하는 오류는 매우 명백합니다. (Q6-10에서 볼 오류는 약간 다르기 때문에 다음 오류를 주의하십시오.)
    incompatible types: inference variable U has incompatible bounds, 
    equality constraints: java.lang.String 
    lower bounds: java.lang.Integer
    
    문제 5도 엉터리 진술이다.
    이것은 우리가 범용 유형의 대상을 설명할 때 유형 파라미터를 제공해야 하기 때문이다.

    유형 변환 문제


    // Q6 - 10
    (Demo<Integer>) new Demo<String>(strings).map(f);
    (Demo<Number>) new Demo<String>(strings).map(f);
    (Demo<Object>) new Demo<String>(strings).map(f);
    (Demo<String>) new Demo<String>(strings).map(f);
    (Demo) new Demo<String>(strings).map(f);
    
    주요 논리는 이미 이전 절에서 해석되었다.여기서 유일한 차이점은 우리가 형식 변환을 실행하는 것이지 되돌아오는 대상을 변수에 분배하는 것이 아니라는 것이다.또는 반환된 대상을 변수에 부여하기 전에 형식 변환을 합니다.
    Q1-5와 유사하게, 우리가 계속해서 어떤 내용을 되돌리기 전에, 우리는 여전히 String 가 무엇인지 추정해야 한다.값 부여 문장과 달리 컴파일러는 추정할 수 있는 변수가 없습니다.따라서 두 번째 전략, 즉 방법 매개 변수로 추정하는 것을 사용할 것이다.
    기억해라, 우리는 있다Integer따라서 유일하게 언급해야 할 것은U이다.map(<Object super String, ? extends U>)?로 결정되었으므로 ?Integer여야 합니다.
    이제 저희가 생겼어요.
    Demo<Integer> map(Function<Object super String, Integer extends Integer> f) 
    
    UInteger이므로 U 방법의 반환 유형은 Integer 으로 변경됩니다.
    범형이 변하지 않는다는 것을 알면 Q6(과 Q10)를 제외한 나머지 문장에 문제가 있다는 것을 알게 될 것이다.
    Q7 데모 사용
    // ERROR
    (Demo<Number>) Demo<Integer>  ...
    
    // Or view it as 
    List<Integer> listOfIntegers = new ArrayList<Integer>();
    listOfIntegers.add(1);
    listOfIntegers.add(2);
    Demo<Number> list = new Demo<Integer>(listOfIntegers); 
    
    컴파일러 오류:
    incompatible types: Demo<java.lang.Integer> 
    cannot be converted to Demo<java.lang.Number>
    
    Q10에 대해 컴파일할 수 있지만, 이것은 좋은 방법이 아니다. 왜냐하면 우리는 되돌아오는 대상을 rawtype에 분배하기 때문이다.
    List<Integer> listOfIntegers = new ArrayList<Integer>();
    listOfIntegers.add(1);
    listOfIntegers.add(2);
    // View it as 
    Demo list = new Demo<Integer>(listOfIntegers); 
    
    현재mapDemo<Integer> 유형에 속한다.

    대상 유형 및 유형 증명


    나의 위의 해석은 Oracle's java tutorial에서 소개한 기술 용어를 사용하지 않았다.다음은 내가 어떻게 정확한 용어를 사용하여 그것을 해석할 것인가이다.list
  • 이 문장(LHS)에 필요한 인스턴스Demo
  • Demo<Number> list = new Demo<String>(strings).map(f); 타겟 유형입니다.
  • 방법 매핑(f)이 유형Demo<Number>의 값을 되돌려주기 때문에 컴파일러는 유형 매개 변수Demo<Number>가 반드시 값Demo<U>이어야 한다고 추정한다
  • 또는, 우리도 방법 명칭 전에 유형을 명시적으로 증명할 수 있다U
  • 공구서류


    Oracle's Java tutorial on Type Inference

    좋은 웹페이지 즐겨찾기