JAVA 범 형 을 깊이 이해 하 다.

20721 단어 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 팬 형 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 해 주 십시오!

좋은 웹페이지 즐겨찾기