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


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>사이에 static및 void메소드 서명에 추가됨 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.