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


247

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

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

답변:


136

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

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

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


183

" 모든 클래스의 모든 인스턴스는 해당 클래스 유형의 동일한 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가 인스턴스와 바인딩되어 있기 때문입니다.

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

와일드 카드 및 서브 타이핑

제네릭 메서드의 형식 인수


2
제한된 wildCards에 대한 내 대답 stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar

"Class <T>는 특정 클래스 유형 'T'의 클래스 객체를 나타냅니다." 감사합니다 ..
bynu022

이 답변은 클래스 (Java)에 관한 질문에서 클래스 (학교에서)를 사용하는 방식에 몹시 혼란 스럽습니다. 저자가 문장에서 다른 문장으로 무엇에 대해 이야기하고 있는지 알기가 어렵습니다.
Echox 2019

@Echox 죄송합니다. 구체적인 질문이 있으면 개선 할 수 있습니다.
Kanagavelu Sugumar


32

Java 문서에서 :

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

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

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

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

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

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


2
나는 (생각했다). 내가 작업 한 프레임 워크 (종류)는 모듈이 의존하는 서비스의 클래스 이름을 전달해야했습니다. 선택의 양을 제한하기 위해 Class 객체를 사용하는 레이어를 만들었습니다. Class<? extends X>내가 생각한 표기법을 사용하여 '서비스'유형으로 만 제한 할 수 있다고 생각했습니다. 일반적인 '서비스'유형이 없었기 때문에로만 할 수있었습니다 Class<?>. 아아.
fwielstra

9

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

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

5

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


@SuppressWarnings("unchecked")구조에 온다! (그냥 항상 가능한 한 작은 범위로 적용하도록주의 않는 코드에서 모호한 잠재적 인 문제를.)
DONAL 휄로우

4

@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 컨텐츠 트리 유형의 문서를 리턴 할 수 있습니다 .


2

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


2

자바에서 <T>일반 클래스를 의미합니다. 일반 클래스는 모든 유형의 데이터 유형 에서 작동 할 수있는 클래스입니다. 즉, 데이터 유형과 무관하다고 말할 수 있습니다.

public class Shape<T> {
    // T stands for "Type"
    private T t;

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

여기서 T 는 유형을 의미합니다. 이제이 Shape 클래스의 인스턴스를 만들 때 어떤 데이터 형식이 작동하는지 컴파일러에 알려야합니다.

예:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

정수 는 유형이고 문자열 도 유형입니다.

<T>특히 제네릭 형식을 나타냅니다. Java Docs에 따르면-제네릭 형식은 형식에 대해 매개 변수 가 지정된 일반 클래스 또는 인터페이스입니다 .


0

다른 예를 들기 위해 클래스의 일반 버전 ( Class<T>)을 사용하면 아래와 같은 일반 함수를 작성할 수 있습니다.

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}

-2

처음에는 혼란 스럽습니다. 그러나 아래 상황에서 도움이됩니다.

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.

4
Action a = new Action ()이라고 말하는 것은 매우 복잡한 방법이 아닙니까?
Karl

1
Action a = new Action() ? Action나는 인터페이스, 그것은 SomeAction우리가 인스턴스를 얻으려고 노력하고 있습니다. SomeAction런타임시 사용 가능한 이름 만 있습니다 .
fastcodejava

이것은 타입 검사를 통과하지 못합니다. 자바 컴파일러는 <code> Class.forName ( "SomeAction") </ code>이 <code> Class <Action> </ code> 유형임을 말할 방법이 없습니다. 런타임에 알려야합니다.
tonio

@tonio, 맞습니다. 아마도 첫 번째 줄을 일종의 try / catch로 감싸 야 할 것입니다. 그러나 예외가 발생하지 않는다고 가정하면 두 번째 줄이 작동합니다.
MatrixFrog

2
실제로 설명하는 것은 SomeAction.class가 Class <패턴과 일치한다는 것입니다. extends Action>-즉, useAction (Class <? extends Action> klass) 메소드가있는 경우 useAction (SomeAction.class)을 호출 할 수 있습니다.
토마스 앤드류스

-5

쇠고기 클래스를 사용하십시오.

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}

4
이것은 실제로 질문에 대한 완전한 대답을 제공하지는 않습니다. 추가 할 내용이 있다고 생각되면이 답변을 편집하십시오.
MasterAM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.