경계형 유형 및 와일드카드 - [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
로 교체할 수 있습니다 Shirt
와 Tshirt
모두 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>
의 서랍이 될 수 있다.셔츠 한 벌은 어떤 참조물을 가리키든지 서랍에 넣을 수 있다.이것은 a
super
가 옷이기 때문에 서랍에 들어간 옷도 합리적이다.가져오기 및 배치 지침
일부 관찰 결과:
협방차
Shirt
은Clothing
Object
은Shirt
의 하위 유형입니다.반대로
Shirt
은Clothing
Drawer<Shirt>
은Drawer<? extends Clothing>
의 하위 유형입니다.셔츠법 연구
Java
Clothing
의 일반적인 방법:// 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
로 강제 변환한다면, 컴파일러는 강제 변환을 계속 실행할 수 있습니다. 왜냐하면 컴파일러에 대해 우리는 Drawer
을 Drawer<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>
부터 사용자 정의 유형에 이르기까지 모든 유형을 표시하기 때문이다.모든 유형으로 강제 변환한다고 말하는 것과 특정 유형으로 강제 변환한다고 말하는 것은 약간 다르다.특정 서랍이 가능한 서랍 중 하나라는 것은 확실하다.어떤 가능한 서랍도 특정한 서랍이 아닐 수 있다.다시 오른쪽을 살펴보면
Clothing
은Drawer
Drawer
인 것을 알 수 있다.우리는 이것이 무슨 서랍인지 모른다.그러나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>();
답은 다음과 같습니다.
생각을 끝내다
본고는 소개할 내용이 많고 아직 언급하지 않은 내용이 더 많다.나는 접촉이 증가함에 따라 범형에 대한 이해도 증가할 것이라고 생각한다.나는 유형 지우기와 원시 유형을 상세하게 소개하지 않았다.그 밖에 자바 집합 프레임워크의 상하문에서 범주를 해석하는 것이 매우 도움이 될 수 있기 때문에 나는 확실히 뒤에서 이 점을 토론할 계획이다.
추가 읽기 자료 중 하나는 Java Generics FAQs
Reference
이 문제에 관하여(경계형 유형 및 와일드카드 - [OOP 및 Java#6]), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/tlylt/bounded-generic-types-wildcards-oop-java-6-1mcc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)