경계형 유형 및 와일드카드 - [OOP 및 Java#6]

29858 단어 computerscienceoopjava
범주에 대한 논의를 계속합니다.
  • 경계형 유형
  • <T extends String> void print(T t){}
    
  • 와일드카드
  • Drawer<?> drawer = new Drawer<String>("abc"1);
    

    유계범형 유형


    '유계'라는 단어는 어떤 물건을 가능한 값 범위 내에 제한한다.
    이제 다시 한 번 에서 제시한 점, 즉 우리가 범형을 사용할 때마다, 우리는 가능한 한 많은 번역을 할 때 유형 검사를 필요로 한다.이것은 컴파일할 때 오류가 실행할 때 오류보다 포착하기 쉽기 때문에 유용한 정책이다.만약 우리가 전달하는 대상의 유형이 정확하다는 것을 확보한다면, 번역 후에 우리는 한 가지 걱정할 필요가 없다.
    // for illustration purposes
    class Clothing {};
    class Shirt extends Clothing {};
    class Tshirt extends Shirt {};
    
    class Drawer<T> {
      T obj;
      Drawer(T obj) {
        this.obj = obj;
      }
      T get() {
        return this.obj;
      }
    }
    
    Drawer 대상의 예시와 표시Clothing, Shirt, Tshirt의 클래스를 다시 사용하여 유계 유형 파라미터가 어떻게 작동하는지 봅시다.
    원시 실현에는 제약이 없다.자리 표시자 T 는 기술적으로 가능한 모든 유형의 대상을 수신할 수 있다.
    new Drawer<Clothing>(new Clothing());
    new Drawer<String>(new String());
    new Drawer<Double>(new Double());
    
    그러나 이것은 결코 이상적이지 않을 수도 있다. 왜냐하면

    There may be times when you want to restrict the types that can be used as type arguments in a parameterized type.


    대상을 향한 사상에 따르면 우리는 (예를 들어 Drawer!)에 속하지 않는 서랍을 받아들이기를 원하지 않을 수도 있다.따라서 우리는 자리 표시자를 상계, 예를 들어 Human에 귀속시키려고 할 가능성이 높기 때문에 상기 경계와 하위 클래스의 교체만 받아들일 것이다.예를 들어 Clothing 때문에 <T extends Clothing> 또는 T 심지어 Clothing 로 교체할 수 있습니다 ShirtTshirt 모두 Shirt 계승 관계를 통해).
    class Drawer<T extends Clothing> {
      T obj;
      Drawer(T obj) {
        this.obj = obj;
      }
      T get() {
        return this.obj;
      }
    }
    
    // usage
    new Drawer<Double>(new Double()); // ERROR
    new Drawer<Clothing>(new Clothing()); // SAFE
    new Drawer<Shirt>(new Shirt()); // SAFE
    new Drawer<Tshirt>(new Tshirt()); // SAFE
    
    Tshirt 키워드를 사용하여 경계형 유형을 실현합니다.주의해라, 여러 개의 경계가 있을 수도 있다.
    // has the same effect of only extending Clothing
    Drawer<T extends Clothing & Shirt & Tshirt> {
      T obj;
      Drawer(T obj) {
        this.obj = obj;
      }
      T get() {
        return this.obj;
      }
    }
    
    유계 유형에 대한 또 다른 점은 유계가 컴파일할 때 허용하는 방법 호출 수를 증가시킨다는 것이다.경계가 없습니다. 컴파일러는 자리 표시자가 가능한 유형 중 하나일 수 있다는 것만 알고 있습니다.따라서 자리 표시자 형식의 대상에 대해 어떤 방법을 사용해도 성공할 수 없습니다.
    만약 당신이 Clothing 파라미터를 받아들이는 방법을 지정하고 extends 를 작성하려고 시도한다고 가정하십시오.모든 대상에 T t 방법이 있는 것은 아니기 때문에 컴파일러 코드는 안전하지 않다.모든 객체가 t.length() 의 하위 클래스가 아니면 Java의 모든 객체가 length() 클래스에서 지정한 방법을 지원합니다.이것은 전화Object 또는 Object로 연결하면 된다는 것을 의미한다.
    경계 형식에 대해 현재 컴파일러는 자리 표시자에 들어간 대상이 형식/하위 형식 중 하나가 될 것을 알고 있습니다.그리고 경계에서 지정한 모든 방법을 안전하게 호출할 수 있습니다.

    와일드카드


    어댑터를 깊이 연구하기 전에 우리는 복잡한 유형과 간단한 유형의 개념을 되돌아봐야 한다
    // examples of simple
    String s;
    Clothing c;
    Shirt st;
    
    // examples of complex
    String[] s;
    Clothing[] c;
    Shirt[] st;
    
    Java 배열은 복잡합니다. 배열은 협동하기 때문에 다음과 같은 특징을 얻을 수 있습니다.
    Clothing c = new Shirt(); // SAFE
    Clothing[] c = new Shirt[1](); // SAFE
    
    우리가 atoString()의 예를 사용할 때, 우리는 용기의 이 개념을 처리하고 있다.그러면 항목을 집합에 저장하는 수조처럼 다음 방법이 작동할 수 있습니까?
    Clothing c = new Shirt();
    Drawer<Clothing> c = new Drawer<Shirt>(new Shirt());
    
    이전에 우리가 범주형은 변하지 않는다고 언급한 것은 간단한 유형의 하위 유형 관계가 복잡한 유형으로 확장되지 않는다는 것을 의미한다.이것도 우리가 등호 오른쪽의 유형을 생략할 수 있는 이유다.
    // SAFE
    Drawer<Clothing> c = new Drawer<Clothing>(new Clothing); 
    // ALSO SAFE
    Drawer<Clothing> c = new Drawer<>(new Clothing): 
    
    만약 토론이 여기서 끝난다면, 우리는 어댑터를 필요로 하지 않을 것이다.그러나 아형 관계는 충분히 합리적인 것 같다.자바 수조에 그것이 있고 많은 상황에서 다태성을 지원하는 것이 도움이 된다는 것을 감안하여 우리는 어댑터를 사용하여 범주형의 같은 효과를 실현할 수 있다.
    옷을 넣는 서랍도 결국 셔츠를 넣는 서랍이다.
    우리는 제한을 피하기 위해 어댑터를 사용하거나 equals() 로 표시할 것이다.
    // SAFE
    Drawer<?> d = new Drawer<Clothing>(new Clothing); 
    d = new Drawer<Shirt>(new Shirt());
    
    // ALSO SAFE
    Drawer<Shirt> s = new Drawer<Shirt>(new Shirt());
    Drawer<?> anyDrawer = s;
    
    이해를 위해 t 를 임의로 볼 수 있다.그래서 어떤 서랍이든 옷을 넣는 서랍일 수도 있고 셔츠를 넣는 서랍일 수도 있다.

    유계 어댑터


    우리가 Drawer 를 사용할 때, 우리는 서랍에서 물건을 꺼낼 때 실행할 수 있는 방법의 호출 수량을 잃어버릴 것이다.서랍은 어떤 종류든지 있을 수 있기 때문에 서랍에서 나오는 물건에 대한 유일한 보증은 그것들이 ? 종류라는 것이다.따라서 우리는 ?급 방법, 예를 들어 <?> 또는 Object만 호출할 수 있다.익숙한 단어?
    어댑터에 귀속을 추가하면 우선 자리 차지 문자의 유형 범위에 들어갈 수 있는 것을 제한하고 허용된 방법 호출 수를 늘릴 수 있습니다.
    // upper-bounded wildcard
    // SAFE
    Drawer<? extends Shirt> drawerOfShirt;
    drawerOfShirt = new Drawer<Shirt>(new Shirt());
    Shirt s = drawerOfShirt.get();
    drawerOfShirt = new Drawer<Tshirt>(new Tshirt());
    Shirt s = drawerOfShirt.get();
    
    // ERROR
    drawerOfShirt = new Drawer<Clothing>(new Clothing());
    Shirt s = drawerOfShirt.get();
    
    Object 유형 toString() 의 객체와 하위 유형만 허용됩니다.이것은 매우 합리적이다. 왜냐하면 우리는 서랍 안의 티셔츠가 여전히 서랍 안의 셔츠라고 상상할 수 있기 때문이다.그래서 우리가 서랍에서 물건을 꺼낼 때, 그것은 티셔츠일 수도 있고, 그것은 여전히 셔츠일 수도 있다.equals()유형이나 Drawer<? extends Shirt>의 하위 유형이 아니기 때문에 제약의 제한을 받으면 작용하지 않습니다.논리적으로 말하자면, 우리는 그것이 사실이라는 것을 허락할 수 없다. 왜냐하면 Shirt 에서 바지를 꺼내면, 그것은 여전히 옷이지만, 그것은 셔츠가 아니기 때문이다.
    또한 키워드 Clothing 를 사용하여 하한선에 대해 동일한 작업을 수행할 수 있습니다.
    // suppose Drawer contains an update method
    class Drawer<T> {
      T obj;
      //...
      void update(T obj) {
        this.obj = obj;
      }
    }
    
    // lower-bounded wildcard
    // SAFE
    Drawer<? super Shirt> drawer= new Drawer<Shirt>(new Shirt());
    drawer.update(new Shirt());
    drawer = new Drawer<Clothing>(new Clothing);
    drawer.update(new Shirt()); // still safe
    
    현재 서랍은 Shirt, Shirt, 짝수Drawer<Clothing>의 서랍이 될 수 있다.
    셔츠 한 벌은 어떤 참조물을 가리키든지 서랍에 넣을 수 있다.이것은 asuper가 옷이기 때문에 서랍에 들어간 옷도 합리적이다.

    가져오기 및 배치 지침


    일부 관찰 결과:

    협방차

  • ShirtClothing
  • 의 하위 유형입니다.
  • ObjectShirt의 하위 유형입니다.
  • 반대로

  • ShirtClothing
  • 의 하위 유형입니다.
  • Drawer<Shirt>Drawer<? extends Clothing>의 하위 유형입니다.
  • 셔츠법 연구


    JavaClothing의 일반적인 방법:
  • 매개변수 객체가 자신과 동일한지 확인
  • 같은 유형의 경우 일부 사용 가능한 방법을 바탕으로 사용자 정의 비교를 실행하고 사용 가능한 방법을 호출하기 전에 유형 변환이 필요합니다
  • 같은 유형이 아니면 서로 다른 결론을 얻는다
  • // suppose a string is equal to another string if 
    // they have the same length
    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      } else if (obj instanceof String) {
        String s = (String) obj;
        return this.length() == s.length();
      } else {
        return false;
      }
    }
    
    범형에 대해 다음과 같은 불완전한 정확한 실현을 따르는 것은 논리에 부합된다.
    // suppose a drawer is equal to another drawer if
    // they have the same content
    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      } else if (obj instanceof Drawer<T>) {
        Drawer<T> anyDrawer = (Drawer<T>) obj;
        return this.get().equals(anyDrawer.get());
      } else {
        return false;
      }
    }
    
    위의 코드에는 두 가지 오류가 있습니다.
  • 의 불법 범용 유형Drawer<Clothing>
  • 유형 삭제로 인해 범용 유형은 실행할 때 접근할 수 없습니다. 이것은 범용 유형이 검사 후 삭제되는 것을 가리킵니다.실행할 때 Drawer<? super Shirt>equals 는 같고 유형이 없다(원시 유형이라고도 부른다).이렇게 하는 이유는 범주를 도입하기 전에 존재하는 자바 코드를 뒤로 호환할 수 있기 때문이다.자세한 내용 here
  • 검사되지 않거나 안전하지 않은 작업
  • 앞의 오류는 비교된 대상이 @Override 유형에 속하는지 확인할 수 없다는 것을 알려줍니다. 여기서 T를 특정 유형으로 볼 수 있습니다.만약 equals 조건이 유효하다면, 우리는 강제 전환을 진행할 것이다
  • Drawer<T> otherDrawer = (Drawer<T>) obj;
    
    오른쪽은 instanceof 이 아닌 원래 유형 Drawer<Clothing> 으로 변환하는 것으로 볼 수 있습니다.이것은 매우 위험할 수 있다. 왜냐하면 우리가 Drawer 를 가지고 있다고 가정하면, 우리는 Drawer<T> 유형이라는 것을 알기 때문이다.현재, 만약 우리가 T가 if 일 때 그것을 Object 로 강제 변환한다면, 컴파일러는 강제 변환을 계속 실행할 수 있습니다. 왜냐하면 컴파일러에 대해 우리는 DrawerDrawer<T> 로 강제 변환하고 있기 때문입니다.그러나 우리는 Drawer<Stationary> 지향Drawer이 있는데 이것은 좋지 않다.주의, 우리는 어떠한 경계도 사용하지 않았기 때문에 범형의 표준 불변 특징은 반드시 적용해야 한다.
    이곳의 해결 방안은 어댑터를 사용하는 것이다.
    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      } else if (obj instanceof Drawer) {
        Drawer<?> anyDrawer = (Drawer<?>) obj;
        return this.get().equals(anyDrawer.get());
      } else {
        return false;
      }
    }
    
    그것이 현재 작용하는 이유는 instanceof Drawer 여기에서 Drawer<T> 부터 사용자 정의 유형에 이르기까지 모든 유형을 표시하기 때문이다.모든 유형으로 강제 변환한다고 말하는 것과 특정 유형으로 강제 변환한다고 말하는 것은 약간 다르다.특정 서랍이 가능한 서랍 중 하나라는 것은 확실하다.어떤 가능한 서랍도 특정한 서랍이 아닐 수 있다.
    다시 오른쪽을 살펴보면 ClothingDrawerDrawer인 것을 알 수 있다.우리는 이것이 무슨 서랍인지 모른다.그러나cast에서 우리는 그것을 어떤 종류의 서랍을 포함할 수 있는 서랍에 투사시켰다. 이것은 거의 마치'어떤 물건이 서랍이라면 투사하라'는 것과 유사하다.이것은 합리적인 전환이다.컴파일러는 지금 불평하지 않을 것이다. 왜냐하면 너는 이곳에서 매우 얼토당토않기 때문이다.부작용은 주조 후에 가장 흔히 볼 수 있는 방법Drawer<Clothing> otherDrawer급, 예를 들어 Drawer<Stationary>?만 상향 조정할 수 있다는 것이다.

    연습


    다음 문장에 대해 그것들은 번역합니까?
    // for illustration purposes
    class Clothing {};
    class Shirt extends Clothing {};
    class Tshirt extends Shirt {};
    
    class Drawer<T> {
      Drawer(){};
    }
    
    1. Drawer<? extends Shirt> s = new Drawer<Tshirt>();
    2. Drawer<? super Clothing> s = new Drawer<Shirt>();
    3. Drawer<?> s = new Drawer<Double>();
    4. Drawer<Clothing> s = new Drawer<>();
    5. Drawer<Shirt> s = new Drawer<Clothing>();
    6a. Drawer<Tshirt> ts = new Drawer<Tshirt>();
    6b. ts = new Drawer<Shirt>();
    7a. Drawer<?> ws = new Drawer<Clothing>();
    7b. ws = new Drawer<Tshirt>();
    

    답은 다음과 같습니다.

  • 6a입니다.예
  • 제6b호.아니오
  • 은 7a입니다.예
  • 은 7b입니다.예
  • 아니요
  • 생각을 끝내다


    본고는 소개할 내용이 많고 아직 언급하지 않은 내용이 더 많다.나는 접촉이 증가함에 따라 범형에 대한 이해도 증가할 것이라고 생각한다.나는 유형 지우기와 원시 유형을 상세하게 소개하지 않았다.그 밖에 자바 집합 프레임워크의 상하문에서 범주를 해석하는 것이 매우 도움이 될 수 있기 때문에 나는 확실히 뒤에서 이 점을 토론할 계획이다.
    추가 읽기 자료 중 하나는 Java Generics FAQs

    좋은 웹페이지 즐겨찾기