JAVA 범 형 을 깊이 이해 하 다.
범 형의 개념:자바 범 형(generics)은 JDK 1.5 에서 도입 한 새로운 특성 으로 범 형 은 컴 파일 시의 유형 안전 모니터링 체 제 를 제공 합 니 다.이 체 제 는 우리 가 컴 파일 할 때 불법 유형 데이터 구 조 를 감지 할 수 있 도록 합 니 다.
범 형의 본질은 바로 유형 매개 변수 화,즉 조작 한 데이터 형식 이 하나의 매개 변수 로 지정 되 는 것 이다.
일반적인 장점 사용:
1 컴 파일 기간 에 형식 검 사 를 제공 하 였 습 니 다.
2 데 이 터 를 가 져 올 때 유형 을 바 꿀 필요 가 없다.
범용 클래스,인터페이스
범 형 류
문법:
class < , , ,...> {
private ;
// ...
}
자주 사용 하 는 일반적인 표지:T,E,K,V문법 사용:
< > = new < >();
JDK 1.7 이후 뒤의<>의 구체 적 인 데이터 형식 은 생략 하고 쓰 지 않 을 수 있 습 니 다.간단 한 범 형 클래스 정의:
/**
* T: , ,
* @author rainszj
* 2020/3/19
*/
public class GenericDemo01<T> {
private T value;
public GenericDemo01() {
}
public GenericDemo01(T value) {
this.value = value;
}
@Override
public String toString() {
return "GenericDemo01{" +
"value=" + value +
'}';
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
테스트 해 보기:
public class Test {
public static void main(String[] args) {
//
GenericDemo01<String> genericDemo01 = new GenericDemo01<>("java");
// ,
GenericDemo01<Integer> genericDemo02 = new GenericDemo01<>(1);
// , , Object
// , , ,
// class com.rainszj.GenericDemo01
System.out.println(genericDemo01.getClass());
// class com.rainszj.GenericDemo01
System.out.println(genericDemo02.getClass());
// true
System.out.println(genericDemo01.getClass() == genericDemo02.getClass());
}
}
주의사항:범용 클래스,구체 적 인 데이터 형식 이 지정 되 지 않 으 면 Object 형식 으로 받 습 니 다.
일반적인 형식 매개 변 수 는 클래스 형식,즉 데이터 형식 을 참조 할 수 있 을 뿐 기본 데이터 형식 이 될 수 없습니다.
범 형 유형 은 논리 적 으로 여러 가지 다른 유형 으로 볼 수 있 지만 실제로는 모두 같은 유형 이다.
/**
*
*
* @author rainszj
* 2020/3/19
*/
public class ProductGetter<T> {
//
private T product;
private ArrayList<T> list = new ArrayList<>();
/**
*
*
* @param product
*/
public void addProduct(T product) {
list.add(product);
}
/**
*
*
* @return
*/
public T getProduct() {
return list.get(new Random().nextInt(list.size()));
}
@Override
public String toString() {
return "ProductGetter{" +
"product=" + product +
'}';
}
}
public static void main(String[] args) {
ProductGetter<String> productGetter1 = new ProductGetter<>();
//
String[] products1 = {" ", " ", " ", " "};
//
for (int i = 0, length = products1.length; i < length; i++) {
productGetter1.addProduct(products1[i]);
}
//
String product1 = productGetter1.getProduct();
System.out.println(" ," + product1.toString());
ProductGetter<Integer> productGetter2 = new ProductGetter<>();
// money
Integer[] products2 = {1000, 3000, 10000, 500};
for (Integer money : products2) {
productGetter2.addProduct(money);
}
Integer product2 = productGetter2.getProduct();
System.out.println(" ," + product2.toString());
}
범 형 류 에서 아 이 를 낳다.자 류 도 범 형 류 이다.자 류 의 범 형 표지 T 는 부 류 의 범 형 표지 T 와 일치 하거나 관 계 를 포함 해 야 한다.자 류 의 범 형 표 지 는 부 류 를 포함 하 는 범 형 표 지 를 포함한다.
class ChildGeneric<T> extends ParentGeneric<T>
class ChildGeneric<T, E> extends ParentGeneric<T>
하위 클래스 는 일반적인 클래스 가 아니 므 로 부모 클래스 는 일반적인 데이터 형식 을 명 확 히 해 야 한다.
class ChildGeneric extends ParentGeneric<String>
범용 인터페이스문법:
interface < , ,...> {
();
}
범용 인 터 페 이 스 를 실현 하 는 클래스 는 범용 클래스 가 아니 므 로 범용 인 터 페 이 스 를 명확 하 게 실현 하 는 데이터 형식 이 필요 하 다
public class Apple implements Generic<String> {}
실현 류 도 범 형 류 이다.실현 류 와 인터페이스의 범 형 유형 이 일치 하거나 관 계 를 포함 하고 실현 류 의 범 형 표 지 는 범 형 인터페이스의 범 형 표 지 를 포함한다.
public class Apple<K> implements Generic<K> {}
public class Apple<K, V> implements Generic<K> {}
범용 인터페이스 정의
public interface Generic<K> {
K getKey();
}
실현 방법:
/**
* , ,
*
*/
public class Pair<K, V> implements Generic<K> {
private K key;
private V value;
public Pair() {
}
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
public V getValue() {
return value;
}
@Override
public String toString() {
return "Pair{" +
"key=" + key +
", value=" + value +
'}';
}
}
테스트:
public class MyTest {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>(" ", 100);
System.out.println(pair.toString());
// Pair{key= , value=100}
}
}
일반적인 방법일반 범 형 방법
범 형 류 는 실례 화 류 에서 범 형의 구체 적 인 유형 을 가리킨다.
범 형 방법 은 방법 을 호출 할 때 범 형의 구체 적 인 유형 을 가리킨다.
문법:
<T,E,...> ( ) {
// ...
}
public
와 반환 값 중간 <T,E,...>
(범 형 목록)은 매우 중요 하 므 로 이 방법 을 범 형 방법 으로 설명 하 는 것 으로 이해 할 수 있다.성명
<T,E,...>
의 방법 만 이 범 형 방법 이 고 범 형 류 에서 범 형 을 사용 한 구성원 방법 은 범 형 방법 이 아니다.<T>
이 방법 은 범 형 T 를 사용 할 것 임 을 나타 내 는데 이때 만 범 형 T 를 사용 할 수 있다.
public class ProductSetter<T> {
private T product;
private Random random= new Random();
private ArrayList<T> list = new ArrayList<>();
public void addProduct(T product) {
list.add(product);
}
/**
* @param list
* @param <E> ,
* @return
*/
public <E> E getProduct(ArrayList<E> list) {
return list.get(random.nextInt(list.size()));
}
public T getProduct() {
return list.get(random.nextInt(list.size()));
}
@Override
public String toString() {
return "ProductSetter{" +
"product=" + product +
'}';
}
}
테스트:
public static void main(String[] args) {
ProductSetter<String> productSetter = new ProductSetter<>();
String[] products1 = {" ", " ", " ", " "};
for (int i = 0; i < products1.length; i++) {
productSetter.addProduct(products1[i]);
}
System.out.println(productSetter.getProduct());
ArrayList<String> list1 = new ArrayList<>();
list1.add(" ");
list1.add(" ");
list1.add(" ");
String product1 = productSetter.getProduct(list1);
System.out.println(product1 + "\t" + product1.getClass().getSimpleName());
// String
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
Integer product2 = productSetter.getProduct(list2);
System.out.println(product2 + "\t" + product2.getClass().getSimpleName());
// 2 Integer
}
정적 범 형 방법
public static <T, E, K> void pringType(T k1, E k2, K k3) {
System.out.println(k1 + "\t" + k1.getClass().getSimpleName());
System.out.println(k2 + "\t" + k2.getClass().getSimpleName());
System.out.println(k3 + "\t" + k3.getClass().getSimpleName());
}
//
ProductSetter.pringType(1, "hello", false);
//출력 결과1 Integer
hello String
false Boolean
주의:
// ,
public class Test<T> {
//
//
public static T getKey(T key) {
return key;
}
//
//
public static <E> E getKey(E key) {
return key;
}
}
범 형 방법 중의 가 변 매개 변수
public class MyTest {
public static void main(String[] args) {
MyTest.print(1, 2, 3);
}
/**
*
* @param value
* @param <E>
*/
public static <E> void print(E ... value) {
for (int i = 0; i < value.length; i++) {
System.out.println(value[i]);
}
}
}
요약:범 형 방법 은 방법 을 유형 에 독립 시 켜 변 화 를 일 으 킬 수 있다.
만약
static
방법 이 범 형 능력 을 사용 하려 면 반드시 범 형 방법 이 되 어야 한다.형식 마스크
유형 어댑터 는 일반적으로 구체 적 인 유형 실 참 대신
?
을 사용한다.타 입 어댑터 는 타 입 인삼 이지 타 입 인삼 이 아 닙 니 다.
우 리 는 먼저 간단 한 범 형 류 를 정의 합 니 다.
public class Box<T> {
private T width;
public static void showBox(Box<Number> box) {
Number width = box.getWidth();
System.out.println(width);
}
public T getWidth() {
return width;
}
public void setWidth(T width) {
this.width = width;
}
}
main 방법:
public static void main(String[] args) {
Box<Number> box1 = new Box<Number>();
box1.setWidth(100);
showBox(box1);
}
우리 가 main 방법 에서 이 코드 를 추가 하면 오류 가 발생 할 것 이다.
Box<Integer> box2 = new Box<>();
box2.setWidth(200);
showBox(box2);
Integer 류 는 Number 류 에서 계승 되 지만 유형 어댑터 에는 계승 이라는 개념 이 존재 하지 않 습 니 다!방법 을 다시 불 러 올 수 있 지만 같은 범 형 류 에서 서로 다른 데이터 형식 에 따라 만 든 대상 은 본질 적 으로 같은 유형 이 고 같은 종류의 템 플 릿 을 공용 하기 때문에 방법의 재 부팅 을 통 해 서로 다른 범 형 유형 을 전달 할 수 없습니다.
이 때 유형 마스크
?
를 사용 하여 구체 적 인 유형 실 참 을 대표 할 수 있 습 니 다!
public static void showBox(Box<?> box) {
Object width = box.getWidth();
System.out.println(width);
}
유형 마스크 의 상한 선우리 위의 쇼 박스()코드 에서
box.getWidth()
가 얻 은 것 이 Object 형식 인지 발견 되 었 습 니 다.이것 은 우리 가 유형 어댑터 를 사용 하지 않 고 얻 은 결과 와 같 습 니 다.이때 우 리 는 유형 마스크 의 상한 선 을 사용 할 수 있다.문법:
/ <? entends >
이 범 형의 유형 을 요구 하 는 것 은 실제 인삼 유형 이나 실제 인삼 유형의 하위 유형 일 수 밖 에 없다.
public static void showBox(Box<? extends Number> box) {
Number width = box.getWidth();
System.out.println(width);
}
public static void main(String[] args) {
Box<Integer> box2 = new Box<>();
box2.setWidth(200);
showBox(box2);
}
형식 마스크 의 하한 선 을 사용 합 니 다.이 유형 이 구체 적 으로 지정 한 유형 인지,이 유형의 하위 유형 인지 알 수 없 기 때문에 List 집합 에서 이 클래스 나 이 클래스 를 추가 하 는 작업 을 수행 할 수 없습니다!
public static void showAnimal(List<? extends Cat> list) {
//
list.add(new Cat());
list.add(new MiniCat());
}
형식 어댑터 의 하한 선문법
/ <? super >
이 범 형 을 요구 하 는 유형 은 실제 인삼 유형 이나 실제 인삼 유형의 부모 유형 만 있 을 수 있 습 니 다.다음은 TreeSet 집합 중의 한 구조 방법 을 통 해 유형 마스크 의 하한 선 을 더욱 이해 하 겠 습 니 다.
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
우선 하나의 Animal 클래스 입 니 다.name 속성 만 있 습 니 다.
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
그리고 하위 클래스,Cat 속성 추가:age
public class Cat extends Animal {
private int age;
public Cat(String name, int age) {
super(name);
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
'}';
}
}
마지막 으로 Cat 의 하위 클래스,MiniCat,속성 level 추가
public class MiniCat extends Cat {
private int level;
public MiniCat(String name, int age, int level) {
super(name, age);
this.level = level;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
@Override
public String toString() {
return "MiniCat{" +
"level=" + level +
'}';
}
}
테스트,우선 우 리 는 MyTest 류 에서 정적 내부 류 의 방식 을 통 해 비교 인 터 페 이 스 를 실현 하고 TreeSet 를 구성 할 때 비교 기 를 전달 해 야 한다.
public class MyTest {
public static void main(String[] args) {
//
// TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator1());
//
TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator2());
//
// TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator3());
List<Cat> list = Arrays.asList(new Cat("a", 12), new Cat("c", 9), new Cat("b", 20));
animals.addAll(list);
animals.forEach(System.out::println);
}
public static class Comparator1 implements Comparator<Animal> {
@Override
public int compare(Animal o1, Animal o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static class Comparator2 implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
return o1.getAge() - o2.getAge();
}
}
public static class Comparator3 implements Comparator<MiniCat> {
@Override
public int compare(MiniCat o1, MiniCat o2) {
return o1.getLevel() - o2.getLevel();
}
}
}
결론:이상 의 비 교 를 통 해 알 수 있 듯 이 유형 어댑터 의 하한 선 은 실제 인삼 유형 이나 실제 인삼 유형의 부모 유형 만 전달 할 수 있다.
우리 가 매번 비교 할 때마다 Cat 유형 을 사용 하지만
Comparator1
은 Animal 의 name 속성 을 비교 합 니 다.이것 은 우리 가 Cat 대상 을 초기 화 할 때 반드시 Animal 대상 을 초기 화 합 니 다.즉,하위 대상 을 만 들 때 반드시 부모 대상 을 먼저 만 들 기 때문에 비교 할 수 있 습 니 다.유형 마스크 를 사용 하 는 상한 선 이 라면 대상 을 만 들 때 이 클래스 의 하위 클래스 대상 의 속성 을 비교 하면 빈 포인터 이상 이 발생 합 니 다!즉
Comparator3
사용 이 불가능 한 이유 로TreeSet
에서 만 사용 <? super E>
,유형 어댑터 의 하한 선 을 사용 할 수 있다.형식 지우 기
범 형 은 자바 1.5 가 도입 한 개념 으로 그 전에는 범 형 이 없 었 지만 범 형 코드 는 이전의 코드 와 잘 호 환 될 수 있 었 다.그것 은 범 형 정 보 는 컴 파일 단계 만 존재 하기 때문에 JVM 에 들 어가 기 전에 범 형 과 관련 된 정보 가 지 워 지기 때문에 우 리 는 유형 지우 기 라 고 부른다.
무한 형식 지우 기
먼저 일반적인 클래스 를 정의 합 니 다:
public class Erasure<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
출력 구조:
public static void main(String[] args) {
Erasure<Integer> erasure = new Erasure<>();
Class<? extends Erasure> cls = erasure.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + ":" + field.getType().getSimpleName()); // key:Object
}
}
컴 파일 이 끝 난 바이트 파일 에서 T-->Object 형식 을 발견 할 수 있 습 니 다.유한 형식 지우 기
아니면 아까 의 범 형 류 는 범 형의 상한 선 을 넣 었 을 뿐이다.
public class Erasure<T extends Number> {// ...}
테스트 가 변 하지 않 습 니 다.출력 결과:key:Number
우리 가 범 형의 상한 선 을 지 정 했 을 때,그것 은 우리 의 범 형 을 상한 유형 으로 지 울 것 이다.
마찬가지 로 범 형 방법 에 대해 서도 마찬가지 의 이치 이다.
//
public <E extends List> E test(E t) {
return t;
}
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
//
// getKey:Number
// test:List
// setKey:void
브리지 방법범용 인터페이스
public interface Info<T> {
T test(T value);
}
범용 인터페이스의 실현 클래스
public class InfoImpl implements Info<Integer> {
@Override
public Integer test(Integer value) {
return value;
}
}
테스트
public static void main(String[] args) {
Class cls = InfoImpl.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
}
// :
// test:Integer
// test:Object
원래 InfoImpl 에 서 는 Info 인터페이스 중의 한 방법 만 실 현 했 을 뿐 반 사 를 통 해 두 가지 방법 을 얻 었 다.그 중에서 반환 값 이 Object 인 방법 은 바로 브리지 방법 이다.컴 파일 이 끝 난 후에 형식 이 지 워 진 결 과 는 다음 과 같 습 니 다.
public interface Info {
Object test(Object value);
}
public class InfoImpl implements Info {
public Integer test(Integer value) {
return value;
}
// :
@Override
public Object test(Object value) {
return (Integer)value;
}
}
범용 배열개발 중 에 일반적으로 상용 하 는 것 은 범 형 집합 이다
일반 배열 의 생 성:
범 형 배열 의 인용 을 설명 할 수 있 지만 범 형 배열 대상 을 직접 만 들 수 는 없습니다.
java.lang.reflect.Array
의 newInstance(Class<T>, int)
을 통 해 T[]배열 을 만 들 수 있 습 니 다.
//
ArrayList<String>[] arrayLists1 = new ArrayList[3];
//
ArrayList<String>[] arrayLists2 = new ArrayList<String>[3];
간단하게java.lang.reflect.Array
의newInstance(Class<T>, int)
를 사용 하여 T[]배열 을 만 듭 니 다.일반적인 배열 을 밀봉 하 다.
public class GenericArray<T> {
private T[] array;
public GenericArray(Class cls, int length) {
this.array = (T[]) Array.newInstance(cls, length);
}
public void put(int index, T item) {
this.array[index] = item;
}
public T get(int index) {
return this.array[index];
}
public T[] getArray() {
return this.array;
}
public static void main(String[] args) {
GenericArray<String> ga = new GenericArray<>(String.class, 3);
ga.put(0, " ");
ga.put(1, " ");
ga.put(2, " ");
System.out.println(Arrays.toString(ga.getArray()));
}
}
범 형 과 반사반사
Class
Constructor
반 사 를 통 해 대상 을 만 들 고 범 형 과 범 형 을 가지 지 않 습 니 다.
Class<Cat> catClass1 = Cat.class;
try {
Constructor<Cat> c1 = catClass1.getConstructor();
Cat cat = c1.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
Class catClass2 = Cat.class;
try {
Constructor c2 = catClass2.getConstructor();
Object cat2 = c2.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
이상 은 JAVA 팬 형의 상세 한 내용 을 깊이 이해 하고 JAVA 팬 형 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 해 주 십시오!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JAVA 객체 작성 및 제거 방법정적 공장 방법 정적 공장 방법의 장점 를 반환할 수 있습니다. 정적 공장 방법의 단점 류 공유되거나 보호된 구조기를 포함하지 않으면 이불류화할 수 없음 여러 개의 구조기 파라미터를 만났을 때 구축기를 고려해야 한다...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.