제네릭 클래스의 정적 메소드?


197

Java에서는 다음과 같은 것을 갖고 싶습니다.

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

그러나 나는 얻는다

비 정적 유형 T에 대한 정적 참조를 만들 수 없습니다

기본 용도 이외의 제네릭을 이해하지 못하므로 이해가되지 않습니다. 인터넷에서 주제에 관한 많은 정보를 찾을 수 없었습니다.

비슷한 방식으로 그러한 사용이 가능한지 누군가가 분명히 말할 수 있습니까? 또한, 원래의 시도가 실패한 이유는 무엇입니까?

답변:


270

정적 메소드 또는 정적 필드에는 클래스의 일반 유형 매개 변수를 사용할 수 없습니다. 클래스의 유형 매개 변수는 인스턴스 메소드 및 인스턴스 필드에만 적용됩니다. 정적 필드와 정적 메서드의 경우 클래스의 모든 인스턴스, 다른 유형 매개 변수의 인스턴스간에 공유되므로 특정 유형 매개 변수에 의존 할 수 없습니다.

문제가 클래스의 type 매개 변수를 사용해야하는 것처럼 보이지 않습니다. 당신이하려는 일을 더 자세하게 설명한다면, 우리는 당신이 그것을하는 더 좋은 방법을 찾도록 도울 수 있습니다.


9
이 답변은 실제로 해결 방법을 제공하는 대신 포스터의 문제를 설명합니다.
Jorn

7
@Andre : 당신의 직감은 근거가 없습니다. C #은 실제로 이런 방식으로 제네릭을 처리합니다.
jyoungdev

32
"정적 필드와 정적 메소드의 경우 클래스의 모든 인스턴스, 심지어 다른 유형 매개 변수의 인스턴스 사이에서 공유됩니다 ..." 유형 삭제로 다시 견과류를 걷어차 버렸습니다!
BD at Rivenhill

2
컴파일 후 일반 클래스 / 메서드가 어떻게 보이는지 살펴보면 일반 속성이 제거 된 것을 볼 수 있습니다. 그리고 컴파일 후 List <Integer>는 "List"처럼 보입니다. 따라서 컴파일 후 List <Integer>와 List <Long> 사이에는 차이가 없습니다. 둘 다 List가되었습니다.
Dainius

3
나는 그가 자신이하는 일을 공정하게 설명했다고 생각합니다. 그가 전리품을 흔들려고한다는 것은 분명합니다! 어
astryk

140

Java는 T유형을 인스턴스화 할 때까지 무엇을 알지 못합니다 .

어쩌면 호출하여 정적 메소드를 실행할 수 Clazz<T>.doit(something)있지만 할 수없는 것처럼 들립니다.

일을 처리하는 다른 방법은 type 매개 변수를 메서드 자체에 넣는 것입니다.

static <U> void doIt(U object)

U에 대한 올바른 제한은 없지만 아무것도 아닌 것보다 낫습니다 ....


그런 다음 제약 조건을 지정하지 않고 메서드를 호출합니다. 즉 Clazz <Object> .doIt (object) 대신 Clazz.doIt (object)입니다. 당신은 그것을 괜찮다고 생각합니까?
André Chalella

두 번째 구문에는 더 정확한 구문이 있는데, 컴파일러가 메소드가 호출 된 contaxt에서 리턴 값 유형을 유추 할 수없는 경우에 필요합니다. 즉, 컴파일러가 Clazz.doIt (object)를 허용하면 그렇게하십시오.
skaffman

1
Clazz <Object> .doIt (object)을 시도했는데 컴파일 타임 오류가 발생했습니다! "토큰의 구문 오류, 잘못 배치 된 구문". Clazz.doIt (object)는 경고조차도 잘 작동합니다.
André Chalella

8
Clazz. <Object> doIt (object)를 사용하십시오. C ++의 Clazz <int> :: doIt (1)과 비교할 때 이상한 구문이라고 생각합니다.
Crend King

왜 U에 대한 올바른 제한을받지 못한다고 말합니까?
Adam Burley

46

나는이 같은 문제에 부딪쳤다. Collections.sortJava 프레임 워크에서 소스 코드를 다운로드하여 답변을 찾았습니다 . 내가 사용한 대답 <T>은 클래스 정의가 아닌 메소드에 제네릭 을 넣는 것이 었습니다 .

그래서 이것은 효과가 있었다 :

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

물론 위의 답변을 읽은 후에는 이것이 일반적인 클래스를 사용하지 않고 수용 가능한 대안이라는 것을 깨달았습니다.

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}

8
수락 된 답변은 기술적으로 정확하지만 이것이 실제로 Google 에서이 질문으로 이끌 때 찾고 있었던 것입니다.
제레미 목록

T extends XXX구문 이 포함 된 예를 제공하기위한 소품 .
오우거 시편 33

3
@Chris 어쨌든 제네릭을 사용하고 있기 때문에 모든 타입을 사용할 수도 있습니다 Comparable. <T extends Comparable<? super T>>대신 시도하십시오 .
easoncxz

15

메소드를 선언 할 때 일반 메소드의 구문을 사용하여 원하는 것을 수행 할 수 있습니다 doIt()( 의 메소드 서명 <T>사이에 staticvoid메소드 서명에 추가됨 doIt()).

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

Eclipse 편집기에서 Cannot make a static reference to the non-static type T오류 없이 위의 코드를 수락 한 다음 다음 작업 프로그램으로 확장했습니다 (약식 연령에 적합한 문화 참조로 완료).

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

콘솔을 실행할 때 다음 줄을 콘솔에 인쇄합니다.

그 전리품을 흔들어 'class com.eclipseoptions.datamanager.Clazz $ KC'!!!
그 전리품을 흔들어 'class com.eclipseoptions.datamanager.Clazz $ SunshineBand'!!!


이 경우 두 번째 <T>가 첫 번째 <T>를 숨깁니까?
André Chalella 8:14에

1
@ AndréNeves, 그렇습니다. 두 번째 는 매개 변수 에서 필드를 마스크하는 <T>것과 같은 방식으로 첫 번째 마스크를 마스크합니다 . class C { int x; C(int x) { ... } }xx
Mike Samuel

샘플의 출력을 설명하는 방법 : 실제로 매개 변수 T 값으로 두 가지 유형이 관련되어있는 것 같습니다. T가 실제로 클래스 유형을 숨기고 있다면 매개 변수없이 "전리품 '클래스 com.eclipseoptions.datamanager.Clazz"가 두 번 출력 될 것으로 예상됩니다.
Pragmateek

1
마스킹과는 아무런 관련이 없습니다. 정적 메서드는 단순히 generic 형식 클래스에 바인딩 할 수 없습니다 . 그럼에도 불구하고 고유 한 제네릭 형식과 경계를 정의 할 수 있습니다.
mike

정적 유형을 한 번 정의하고 여러 정적 메소드에 재사용하는 방법이 있습니까? 나는 static <E extends SomeClass> E foo(E input){return input;}다음과 같은 일을하고 싶을 때 각각의 모든 방법에 대한 팬이 아닙니다static <E extends SomeClass>; //static generic type defined only once and reused static E foo(E input){return input;} static E bar(E input){return input;} //...etc...
HesNotTheStig

14

이 구문은 아직 언급되지 않았다고 생각합니다 (인수가없는 메소드를 원할 경우).

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

그리고 전화 :

String str = Clazz.<String>doIt();

이것이 누군가를 돕기를 바랍니다.


1
실제로 (Java 버전을 모르는 경우) <String>변수에 할당하기 때문에 단순히 type 인수를 유추하기 때문에 가능하지 않습니다 . 할당하지 않으면 간단히 추론 Object됩니다.
Adowrath

이것은 완벽한 솔루션입니다.
Prabhat Ranjan

6

그것은 제대로 오류에서 언급 한 : 당신이 비 정적 T 형식에 대한 정적 참조를 만들 수없는 이유는 유형 매개 변수입니다 T유형 인수 등의로 대체 될 수 Clazz<String>또는 Clazz<integer>등 그러나 정적 필드 / 메소드는 모든 비에서 공유 클래스의 정적 객체.

다음 발췌 내용은 문서 에서 발췌 한 것입니다 .

클래스의 정적 필드는 클래스의 모든 비 정적 객체가 공유하는 클래스 수준 변수입니다. 따라서 매개 변수 유형의 정적 필드는 허용되지 않습니다. 다음 클래스를 고려하십시오.

public class MobileDevice<T> {
    private static T os;

    // ...
}

매개 변수 유형의 정적 필드가 허용 된 경우 다음 코드가 혼동됩니다.

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

정적 필드 os는 phone, pager 및 pc에서 공유되므로 실제 os 유형은 무엇입니까? 스마트 폰, 호출기 및 TabletPC 일 수는 없습니다. 따라서 매개 변수 유형의 정적 필드를 작성할 수 없습니다.

chris의 대답 에서 올바르게 지적 했듯이이 경우 클래스가 아닌 메소드와 함께 유형 매개 변수를 사용해야합니다. 다음과 같이 작성할 수 있습니다.

static <E> void doIt(E object) 

3

다음과 같은 것이 당신을 더 가깝게 할 것입니다

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

편집 : 자세한 내용으로 업데이트 된 예

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}

Jason S가 제안한대로.
André Chalella

@Andre 이전 제안은 U가 Clazz를 확장해야한다는 제한을 제공하지 않았습니다
ekj

나는 알지만 제약 조건을 제외하고는 유용한 것을 추가하지 않습니다. 원래 게시물에서 U를 매개 변수로 전달하지 않고이 작업을 수행하고 싶습니다.
André Chalella 16:24에

@Andre 위의 업데이트를 참조하십시오. 제네릭 형식은 메서드 정의에서만 정의되며 메서드를 호출 할 때 전달되지 않아도됩니다.
ekj

@ekj 감사합니다, 지금 이해합니다. 미안, 나는 3 년 전 매일 Java를 할 때이 질문을 제기했다. 나는 이것이 내가 원래 의도 한 것이 아니라는 것을 이해한다고 생각하더라도 당신의 아이디어를 얻었습니다. 그러나 나는 당신의 대답을 좋아했습니다.
André Chalella

2

클래스에 제네릭 형식을 지정하면 JVM은 클래스가 아닌 정의 된 클래스의 인스턴스 만 있다는 것을 알고 있습니다. 각 정의에는 매개 변수화 된 유형 만 있습니다.

제네릭은 C ++에서 템플릿과 같이 작동하므로 먼저 클래스를 인스턴스화 한 다음 지정된 유형의 함수를 사용해야합니다.


2
Java 제네릭은 C ++ 템플릿과는 상당히 다릅니다. 일반 클래스는 자체적으로 컴파일됩니다. 실제로 C ++의 동등한 코드는 작동합니다 (호출 코드는 다음과 같습니다 Clazz<int>::doIt( 5 ))
David Rodríguez-dribeas

C ++ 템플릿 참조와는 별도로이 답변이 더 좋습니다 .C ++ 템플릿 참조와는 별도로 전체 클래스 대신 클래스의 한 인스턴스에 대해서만 일반 유형에 대한 의견이 있습니다. 허용 된 답변은 이것을 설명하지 않으며 정적 메소드에서 클래스의 일반 유형을 사용할 수없는 이유와 관련이없는 해결 방법을 제공합니다.
Jorn

1

또한 그것 때문에 우리가 정의하지만, 그 generics.Which 수단의 "소거"속성의 발생, 간단한 용어를 넣어 ArrayList<Integer>ArrayList<String>컴파일 시간에,이 두 가지 구체적인 유형으로 유지하지만 런타임시 JVM이 제네릭 형식 및 삭제됩니다 두 클래스 대신 하나의 ArrayList 클래스 만 만듭니다. 우리가 일반에 대한 정적 유형 방법 또는 아무것도를 정의 할 때, 그것은 그 일반의 모든 인스턴스에 의해 공유되고, 내 예에서 모두가 공유 ArrayList<Integer>하고 ArrayList<String>거긴 당신은 클래스하지 않음을의 error.A 제네릭 형식 매개 변수를 얻을 이유 정적 컨텍스트에서 허용됩니다!


1

Rivenhill의 @BD :이 오래된 질문이 작년에 다시 주목을 받아 왔기 때문에 토론을 위해 조금만 더 살펴 보도록하겠습니다. doIt메소드 본문은 T특정 작업을 수행하지 않습니다 . 여기있어:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

따라서 모든 유형 변수를 완전히 삭제하고 코드를 작성할 수 있습니다.

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

확인. 그러나 원래 문제에 더 가까이 다가 갑시다. 클래스 선언의 첫 번째 유형 변수는 중복입니다. 이 방법의 두 번째 것만 필요합니다. 여기에 다시갑니다. 그러나 아직 최종 답변은 아닙니다.

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

그러나 다음 버전은 동일한 방식으로 작동하기 때문에 아무것도 아닌 것에 대해 너무 소란 스럽습니다. 메소드 매개 변수의 인터페이스 유형 만 있으면됩니다. 어디에도 유형 변수가 없습니다. 이것이 원래 문제였습니까?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

0

정적 변수는 클래스의 모든 인스턴스에서 공유되므로 예를 들어 다음 코드가있는 경우

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

T는 인스턴스가 작성된 후에 만 ​​사용 가능합니다. 그러나 인스턴스가 사용 가능하기 전에도 정적 메소드를 사용할 수 있습니다. 따라서 제네릭 형식 매개 변수는 정적 메서드 및 변수 내에서 참조 할 수 없습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.