[java] Java에서 Class <T>를 사용하는 방법은 무엇입니까?

제네릭에 대한 좋은 토론 과이 질문 에서 실제로 배후에서 수행하는 작업에 대해 설명합니다 . 따라서 Vector<int[]>정수 배열로 구성된 벡터이며 HashTable<String, Person>키가 문자열 및 값 인 테이블입니다 Person. 그러나 나를 혼란스럽게하는 것은의 사용법입니다 Class<>.

Java 클래스 Class는 템플릿 이름을 가져야합니다 (또는 Eclipse의 노란색 밑줄로 표시됩니다). 거기에 무엇을 넣어야할지 모르겠습니다. Class객체의 요점은 반사에 대한 객체에 대한 정보가 완전히없는 경우입니다. Class객체가 보유 할 클래스를 지정하는 이유는 무엇 입니까? 나는 분명히 알지 못하거나 Class객체를 사용하지 않을 것입니다. 특정한 것을 사용합니다.



답변

클래스 클래스의 생성 된 버전을 사용하면 무엇보다도 다음과 같은 것을 작성할 수 있습니다.

Class<? extends Collection> someCollectionClass = someMethod();

그런 다음 수신하는 Class 객체가 extends이고이 Collection클래스의 인스턴스가 (적어도) Collection인지 확인할 수 있습니다.


답변

모든 클래스의 모든 인스턴스는 해당 클래스 유형의 동일한 java.lang.Class 객체를 공유합니다.

예)

Student a = new Student();
Student b = new Student();

그렇다면 a.getClass() == b.getClass()사실입니다.

이제 가정

Teacher t = new Teacher();

제네릭이 없으면 아래가 가능합니다.

Class studentClassRef = t.getClass();

그러나 이것은 지금 잘못이다 ..?

예) public void printStudentClassInfo(Class studentClassRef) {}로 호출 할 수 있습니다Teacher.class

제네릭을 사용하면 피할 수 있습니다.

Class<Student> studentClassRef = t.getClass(); //Compilation error.

이제 T는 무엇입니까 ?? T는 유형 매개 변수 (유형 변수라고도 함)입니다. 꺾쇠 괄호 (<>)로 구분되며 클래스 이름을 따릅니다.

T는 클래스 파일 작성 중에 선언 된 변수 이름 (임의의 이름 일 수 있음)과 같은 기호 일뿐입니다. 나중에
초기화 중에 T가 유효한 클래스 이름 으로 대체됩니다 ( HashMap<String> map = new HashMap<String>();)

예) class name<T1, T2, ..., Tn>

그래서 Class<T>특정 클래스 유형 ‘의 클래스 개체를 나타내는 T‘.

클래스 메소드가 다음과 같이 알 수없는 유형 매개 변수로 작동해야한다고 가정하십시오.

/**
 * Generic version of the Car class.
 * @param <T> the type of the value
 */
public class Car<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

여기서 T는 CarName 과 같은 String유형으로 사용할 수 있습니다

OR T는 modelNumberInteger 유형으로 사용할 수 있습니다 .

OR T는 유효한 자동차 인스턴스Object유형으로 사용될 수 있습니다 .

위의 내용은 런타임에 다르게 사용할 수있는 간단한 POJO입니다.

컬렉션, 예) List, Set, Hashmap은 T 선언에 따라 다른 객체에서 작동하는 가장 좋은 예입니다. 그러나 일단 T를 String으로 선언하면

예를 들어 HashMap<String> map = new HashMap<String>();String Class 인스턴스 객체 만 허용합니다.

일반적인 방법

일반 메소드는 자체 유형 매개 변수를 도입하는 메소드입니다. 이것은 제네릭 형식을 선언하는 것과 비슷하지만 형식 매개 변수의 범위는 선언 된 메서드로 제한됩니다. 정적 및 비 정적 제네릭 메서드와 제네릭 클래스 생성자가 허용됩니다.

제네릭 메서드의 구문에는 형식 매개 변수, 꺾쇠 괄호가 포함되어 있으며 메서드의 반환 형식 앞에 나타납니다. 일반적인 메서드의 경우 형식 매개 변수 섹션이 메서드의 반환 형식 앞에 나타나야합니다.

 class Util {
    // Generic static method
    public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

 class Pair<K, V> {

    private K key;
    private V value;
}

여기 <K, V, Z, Y>에 반환 유형 앞에 있어야하는 메소드 인수에 사용 된 유형의 선언이 boolean있습니다.

아래에서; <T>클래스 수준에서 이미 선언되었으므로 메서드 수준에서는 형식 선언 이 필요하지 않습니다.

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

그러나 클래스 수준 유형 매개 변수 K, V, Z 및 Y를 정적 컨텍스트 (정적 방법)에서 사용할 수 없으므로 아래는 잘못되었습니다.

class Util <K, V, Z, Y>{
    // Generic static method
    public static  boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

다른 유효한 시나리오는

class MyClass<T> {

        //Type declaration <T> already done at class level
        private  T myMethod(T a){
            return  a;
        }

        //<T> is overriding the T declared at Class level;
        //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. 
        private <T> T myMethod1(Object a){
                return (T) a;
        }

        //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).  
        private T myMethod1(Object a){
                return (T) a;
        }

        // No ClassCastException        
        // MyClass<String> obj= new MyClass<String>();
        // obj.myMethod2(Integer.valueOf("1"));
        // Since type T is redefined at this method level.
        private <T> T myMethod2(T a){
            return  a;
        }

        // No ClassCastException for the below
        // MyClass<String> o= new MyClass<String>();
        // o.myMethod3(Integer.valueOf("1").getClass())
        // Since <T> is undefined within this method; 
        // And MyClass<T> don't have impact here
        private <T> T myMethod3(Class a){
            return (T) a;
        }

        // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
        // Should be o.myMethod3(String.valueOf("1").getClass())
    private  T myMethod3(Class a){
        return (T) a;
    }


        // Class<T> a :: a is Class object of type T
        //<T> is overriding of class level type declaration; 
        private <T> Class<T> myMethod4(Class<T> a){
            return  a;
        }
    }

마지막으로 정적 메소드에는 항상 명시 적 <T>선언이 필요합니다 . 그것은 수업 수준에서 파생되지 않습니다 Class<T>. 클래스 수준 T가 인스턴스와 바인딩되어 있기 때문입니다.

또한 읽기 제네릭에 대한 제한을

와일드 카드 및 서브 타이핑

제네릭 메서드의 형식 인수


답변

Java 문서에서 :

[…] 더 놀랍게도 클래스 클래스가 생성되었습니다. 클래스 리터럴은 이제 유형 토큰으로 기능하여 런타임 및 컴파일 타임 유형 정보를 모두 제공합니다. 이것은 새로운 AnnotatedElement 인터페이스의 getAnnotation 메소드로 예시 된 정적 팩토리 스타일을 가능하게합니다.

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

이것은 일반적인 방법입니다. 인수에서 유형 매개 변수 T의 값을 추론하고 다음 스 니펫에 표시된대로 T의 적절한 인스턴스를 리턴합니다.

Author a = Othello.class.getAnnotation(Author.class);

제네릭 이전에는 결과를 Author로 전송해야했습니다. 또한 실제 매개 변수가 Annotation의 서브 클래스를 나타내는 지 컴파일러가 확인하지 못하게했을 것입니다. […]

글쎄, 나는 이런 종류의 물건을 사용할 필요가 없었습니다. 누군가?


답변

class<T>서비스 레지스트리 조회를 만들 때 유용하다는 것을 알았습니다 . 예 :

<T> T getService(Class<T> serviceClass)
{
    ...
}


답변

다른 답변에서 알 수 있듯이, 이것이 class일반적인 이유는 여러 가지 가 있습니다. 그러나와 함께 사용할 제네릭 형식을 알 방법이없는 경우가 많습니다 Class<T>. 이 경우 단순히 노란색 일식 경고를 무시하거나 사용할 수 있습니다 Class<?>… 내가 그렇게하는 방법입니다.)


답변

@Kire Haglin의 답변에 따라 JAXB 비 정렬 화 에 대한 문서 에서 제네릭 메소드의 추가 예제를 볼 수 있습니다 .

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

unmarshal임의의 JAXB 컨텐츠 트리 유형의 문서를 리턴 할 수 있습니다 .


답변

와 함께 와일드 카드를 사용하려는 경우가 종종 있습니다 Class. 예를 들어을 Class<? extends JComponent>사용하면 클래스가의 하위 클래스임을 지정할 수 있습니다 JComponent. 에서 Class인스턴스를 검색 한 경우 인스턴스를 생성 하기 전에 캐스트를 수행하는 데 Class.forName사용할 수 있습니다 Class.asSubclass.