원시 유형은 무엇이며 왜 사용해서는 안됩니까?


662

질문 :

  • Java의 원시 유형은 무엇이며 왜 새 코드에서 사용해서는 안된다고 자주 들립니까?
  • 원시 유형을 사용할 수없는 경우 대안은 무엇이며 어떻게 더 좋습니까?

자바 튜토리얼은 여전히이 경고를 일으키는 JComboBox를 사용합니다. 어떤 버전의 콤보 상자가이 경고를 발생시키지 않습니까? docs.oracle.com/javase/tutorial/uiswing/components/…
SuperStar

1
원시 유형이 존재하는 이유는 전혀 제네릭이없는 Java 1.4 및 이전 버전과의 하위 호환성 때문입니다.
Jesper

답변:


744

원시 유형은 무엇입니까?

Java 언어 사양은 다음과 같이 원시 유형 을 정의 합니다.

JLS 4.8 원시 유형

원시 유형은 다음 중 하나로 정의됩니다.

  • 함께 제공되는 형식 인수 목록없이 일반 형식 선언의 이름을 사용하여 구성된 참조 형식입니다.

  • 요소 유형이 원시 유형 인 배열 유형입니다.

  • 의 수퍼 클래스 또는 수퍼 인터페이스에서 상속되지 않은 static원시 유형 의 비 멤버 유형 R입니다 R.

다음은 설명하는 예입니다.

public class MyType<E> {
    class Inner { }
    static class Nested { }

    public static void main(String[] args) {
        MyType mt;          // warning: MyType is a raw type
        MyType.Inner inn;   // warning: MyType.Inner is a raw type

        MyType.Nested nest; // no warning: not parameterized type
        MyType<Object> mt1; // no warning: type parameter given
        MyType<?> mt2;      // no warning: type parameter given (wildcard OK!)
    }
}

여기서, MyType<E>A는 파라미터 화 된 형태 ( JLS 4.5 ). 구어체 적으로이 유형을 간단히 간단히 MyType말하지만 일반적으로 이름은 MyType<E>입니다.

mt위 정의의 첫 번째 글 머리 기호에 의해 원시 유형이 있고 컴파일 경고를 생성합니다. inn또한 세 번째 글 머리 기호로 원시 유형이 있습니다.

MyType.Nested이 파라미터 화 된 형태의 멤버 유형에도 불구하고, 매개 변수화 된 유형이 아닌 MyType<E>그 때문에, static.

mt1, mt2둘 다 실제 유형 매개 변수로 선언되므로 원시 유형이 아닙니다.


원시 유형에서 특별한 점은 무엇입니까?

기본적으로 원시 유형은 제네릭이 도입되기 전과 동일하게 작동합니다. 즉, 다음은 컴파일 타임에 전적으로 합법적입니다.

List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!

위의 코드는 정상적으로 실행되지만 다음과 같은 사항이 있다고 가정합니다.

for (Object o : names) {
    String name = (String) o;
    System.out.println(name);
} // throws ClassCastException!
  //    java.lang.Boolean cannot be cast to java.lang.String

이제는 names아닌 것이 포함되어 있기 때문에 런타임에 문제가 발생 합니다 instanceof String.

당신이 원하는 경우 아마도 names에만 포함 String, 당신은 할 수 아마도 여전히 원시 형식을 사용하고 모든 확인 수동으로 add 자신을 한 후 수동으로 주조String에서 모든 항목 names. 더 좋은 방법은 원시 유형을 사용하지 않고 컴파일러가 Java 제네릭의 힘을 활용하여 모든 작업을 수행하도록하는 것입니다 .

List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!

당신이 경우 물론, DO 원하는 names수 있도록 Boolean, 당신은 그것을 선언 할 수 있습니다 List<Object> names, 그리고 위의 코드를 컴파일합니다.

또한보십시오


원시 유형 <Object>은 유형 매개 변수로 사용하는 것과 어떻게 다릅니 까?

다음은 Effective Java 2nd Edition, Item 23 의 인용문 입니다. 새 코드에서 원시 유형을 사용하지 마십시오 .

원시 유형 List과 매개 변수화 유형 의 차이점은 무엇 List<Object>입니까? 느슨하게 말해서 전자는 일반 유형 검사를 선택하지 않았으며 후자는 명시 적으로 컴파일러에게 모든 유형의 객체를 보유 할 수 있다고 말했습니다. List<String>a 유형의 매개 변수에 a 를 전달할 수 있지만 type의 매개 변수에 List전달할 수는 없습니다 List<Object>. 제네릭에 대한 하위 입력 규칙 List<String>이 있으며 원시 유형의 하위 유형 List이지만 매개 변수화 된 유형 의 하위 유형은 아닙니다 List<Object>. 결과적으로 와 같은 원시 유형을 사용하면 유형 안전성을 잃지 List만 매개 변수화 된 유형을 사용하면 그렇지 않습니다List<Object> .

요점을 설명하기 위해 a를 사용 List<Object>하고 a 를 추가하는 다음 방법을 고려하십시오 new Object().

void appendNewObject(List<Object> list) {
   list.add(new Object());
}

Java의 제네릭은 변하지 않습니다. A List<String>는이 아니므로 List<Object>다음은 컴파일러 경고를 생성합니다.

List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!

appendNewObject원시 형식 List을 매개 변수 로 사용하도록 선언 하면 컴파일되어 제네릭에서 얻는 형식 안전성이 손실됩니다.

또한보십시오


원시 유형 <?>은 유형 매개 변수로 사용하는 것과 어떻게 다릅니 까?

List<Object>, List<String>등은 모두 List<?>이므로 그냥 List대신 있다고 말하는 것이 유혹적 일 수 있습니다 . 그러나 중요한 차이점은 다음과 같습니다. a List<E>는 정의 만하기 때문에 add(E)임의의 객체 만에 추가 할 수 없습니다 List<?>. 원시 타입이 있기 때문에 다른 한편으로는, List형태의 안전성이 없습니다, 당신은 할 수있는 add단지에 대해서는 아무것도 List.

이전 스 니펫의 다음 변형을 고려하십시오.

static void appendNewObject(List<?> list) {
    list.add(new Object()); // compilation error!
}
//...

List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!

컴파일러는 잠재적으로 List<?>! 의 형식 불일치를 위반하지 않도록 보호하는 훌륭한 작업을 수행했습니다 . 매개 변수를 원시 유형으로 선언 List list한 경우 코드가 컴파일되고의 유형 불변 값을 위반하게 List<String> names됩니다.


원시 유형은 해당 유형의 삭제입니다.

JLS 4.8로 돌아 가기 :

이 형태로 사용하는 것이 가능하다 소거 파라미터 화 된 형태 또는 유형의 소자 파라미터 화 된 형태 인 어레이 형의 소거한다. 이러한 유형을 원시 유형 이라고합니다 .

[...]

원시 유형의 수퍼 클래스 (각각 수퍼 인터페이스)는 일반 유형의 모든 매개 변수화에 대한 수퍼 클래스 (수퍼 인터페이스)의 삭제입니다.

수퍼 클래스 또는 수퍼 인터페이스에서 상속되지 않은 생성자, 인스턴스 메소드 또는 static원시 유형의 비 필드 C유형은에 해당하는 일반 선언에서 해당 유형의 소거에 해당하는 원시 유형입니다 C.

간단히 말해서, 원시 유형을 사용하면 생성자, 인스턴스 메소드 및 비 static필드 도 지워 집니다.

다음 예를 보자.

class MyType<E> {
    List<String> getNames() {
        return Arrays.asList("John", "Mary");
    }

    public static void main(String[] args) {
        MyType rawType = new MyType();
        // unchecked warning!
        // required: List<String> found: List
        List<String> names = rawType.getNames();
        // compilation error!
        // incompatible types: Object cannot be converted to String
        for (String str : rawType.getNames())
            System.out.print(str);
    }
}

우리는 원료를 사용하는 경우 MyType, getNames그것은 원시를 반환 그래서 잘으로 삭제된다 List!

JLS 4.6 은 다음을 계속 설명합니다.

또한 유형 삭제는 생성자 또는 메서드의 서명을 매개 변수화 된 유형 또는 유형 변수가없는 서명에 매핑합니다. 생성자 또는 메소드 서명 의 삭제는에 지정된 모든 형식 매개 변수 유형의 삭제와 s동일한 이름으로 구성되는 서명 s입니다 s.

메소드 또는 생성자의 서명이 지워지면 메소드의 리턴 유형과 일반 메소드 또는 생성자의 유형 매개 변수도 삭제됩니다.

제네릭 메서드의 서명 지우기에는 형식 매개 변수가 없습니다.

다음 버그 보고서에는 컴파일러 개발자 인 Maurizio Cimadamore와 JLS의 저자 인 Alex Buckley가 이런 종류의 동작이 발생하는 이유에 대한 의견이 포함되어 있습니다. https://bugs.openjdk.java.net/browse / JDK-6400189 . 즉, 사양이 더 간단 해집니다.


안전하지 않은 경우 왜 원시 유형을 사용할 수 있습니까?

JLS 4.8의 또 다른 인용문은 다음과 같습니다.

원시 유형의 사용은 레거시 코드의 호환성에 대한 양보로만 허용됩니다. Java 프로그래밍 언어에 일반성을 도입 한 후에 작성된 코드에서 원시 유형을 사용하는 것은 권장하지 않습니다. 향후 버전의 Java 프로그래밍 언어에서 원시 유형을 사용할 수 없게 될 수 있습니다.

효과적인 Java 2nd Edition 에는 다음이 추가됩니다.

원시 유형을 사용해서는 안된다는 것을 감안할 때 언어 설계자가 왜 허용합니까? 호환성을 제공합니다.

Java 플랫폼은 제네릭이 도입 된 후 20 년이되었을 때 제네릭을 사용하지 않는 엄청난 양의 Java 코드가 존재했습니다. 이 모든 코드가 제네릭을 사용하는 새 코드와 합법적이고 상호 운용 가능한 상태로 유지되는 것이 중요합니다. 매개 변수화 된 유형의 인스턴스를 일반 유형과 함께 사용하도록 설계된 메소드로 전달하는 것이 합법적이어야했습니다. 마이그레이션 호환성으로 알려진이 요구 사항 은 원시 유형을 지원하기로 결정했습니다.

요약하면, 원시 코드는 새로운 코드에서 절대 사용해서는 안됩니다. 항상 매개 변수화 된 유형을 사용해야합니다 .


예외는 없습니까?

불행하게도, Java 제네릭은 통일되지 않기 때문에 새로운 코드에서 raw 유형을 사용해야하는 두 가지 예외가 있습니다.

  • 클래스 리터럴, 예를 들면 List.class,하지List<String>.class
  • instanceof피연산자, 예를 들면 o instanceof Set,하지o instanceof Set<String>

또한보십시오


16
"Java 제네릭이 비 통합"이라는 것은 무엇을 의미합니까?
Carl G

7
두 번째 예외의 경우 구문 o instanceof Set<?>은 원시 유형을 피할 수 있습니다 (이 경우 피상적 ​​임).
Paul Bellora

1
원시 유형은 매우 유용하며 인터페이스를 확장하는 Bean에 대한 JNDI 조회의 경우 상용구 코드를 줄입니다. 이를 n통해 동일한 코드로 각 구현 클래스에 대해 원격 Bean 을 작성할 필요가 해결 됩니다.
djmj

8
"비 통합"은 삭제되었다는 또 다른 방법입니다. 컴파일러는 일반 매개 변수가 무엇인지 알고 있지만 해당 정보는 생성 된 바이트 코드로 전달되지 않습니다. JLS에서는 클래스 리터럴에 유형 매개 변수가 없어야합니다.
Erick G. Hagstrom

2
@OldCurmudgeon 재미 있네요. 나는 공식적으로는 없습니다 의미 하지도 클래스 리터럴 단지로 정의하기 때문에, TypeName.class여기서 TypeName일반 식별자입니다 ( JLS ). 가설 적으로 말하면, 그것은 실제로 어느 쪽이든 될 수 있다고 생각합니다. 어쩌면 단서로서 List<String>.classJLS가 특별히 컴파일러 오류를 호출하는 변형 일 수 있으므로 언어에 추가하면 이것이 사용되는 것으로 예상됩니다.
Radiodef

62

Java의 원시 유형은 무엇이며 왜 새 코드에서 사용해서는 안된다고 자주 들립니까?

원시 유형은 Java 언어의 고대 역사입니다. 태초에 있었다 Collections그들은 개최 Objects아무것도 더 적은 아무것도. Collections필요한 캐스트 에 대한 모든 작업에서 Object원하는 유형으로 캐스트 합니다.

List aList = new ArrayList();
String s = "Hello World!";
aList.add(s);
String c = (String)aList.get(0);

대부분의 시간 동안 작동했지만 오류가 발생했습니다.

List aNumberList = new ArrayList();
String one = "1";//Number one
aNumberList.add(one);
Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here

오래된 타입리스 컬렉션은 타입 안전을 강제 할 수 없었으므로 프로그래머는 컬렉션 내에 저장된 것을 기억해야했습니다.
이 한계를 극복하기 위해 고안된 제네릭은 개발자가 저장된 유형을 한 번 선언하면 컴파일러가 대신 수행합니다.

List<String> aNumberList = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine

비교하려고:

// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings

비교 가능한 인터페이스가 더 복잡합니다.

//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
   int id;
   public int compareTo(Object other)
   {return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
   int id;
   public int compareTo(MyCompareAble other)
   {return this.id - other.id;}
}

원시 유형으로 CompareAble인터페이스 를 구현하는 것은 불가능합니다 compareTo(MyCompareAble). 왜 사용하지 말아야합니까?

  • Object저장된 모든 제품 Collection을 사용하려면 먼저 캐스팅해야합니다
  • 제네릭을 사용하면 컴파일 시간을 확인할 수 있습니다
  • 원시 유형을 사용하는 것은 각 값을 저장하는 것과 같습니다. Object

컴파일러의 기능 : 제네릭은 이전 버전과 호환되며 원시 유형과 동일한 Java 클래스를 사용합니다. 마술은 대부분 컴파일 타임에 발생합니다.

List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);

다음과 같이 컴파일됩니다 :

List someStrings = new ArrayList();
someStrings.add("one"); 
String one = (String)someStrings.get(0);

이는 원시 유형을 직접 사용한 경우 작성하는 코드와 동일합니다. CompareAble인터페이스에서 어떤 일이 발생하는지 잘 모르겠지만 compareTo, 하나는 a 함수 MyCompareAble와 다른 하나는 Object캐스트 후 첫 번째 함수로 전달하는 두 가지 기능을 생성한다고 생각 합니다.

사용 : 원시 유형의 대안은 무엇입니까 제네릭


30

원시 유형은 유형 인수가없는 일반 클래스 또는 인터페이스의 이름입니다. 예를 들어, 일반 Box 클래스는 다음과 같습니다.

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

의 매개 변수화 된 유형을 작성하려면 Box<T>공식 유형 매개 변수에 대한 실제 유형 인수를 제공하십시오 T.

Box<Integer> intBox = new Box<>();

실제 유형 인수가 생략되면 다음과 같은 원시 유형이 작성됩니다 Box<T>.

Box rawBox = new Box();

따라서 Box일반 유형의 원시 유형입니다 Box<T>. 그러나 제네릭이 아닌 클래스 또는 인터페이스 유형은 원시 유형이 아닙니다.

원시 클래스는 JDK 5.0 이전에는 많은 API 클래스 (예 : Collections 클래스)가 일반적이지 않기 때문에 레거시 코드에 표시됩니다. 원시 유형을 사용하는 경우, 당신은 기본적으로 사전 제네릭 동작을 얻을 - A는 Box당신이 제공 Object에요. 이전 버전과의 호환성을 위해 매개 변수화 된 형식을 원시 형식에 할당 할 수 있습니다.

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

그러나 원시 유형을 매개 변수화 된 유형에 지정하면 경고가 표시됩니다.

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

원시 유형을 사용하여 해당 일반 유형에 정의 된 일반 메소드를 호출하면 경고가 표시됩니다.

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

경고는 원시 유형이 일반 유형 검사를 무시하고 안전하지 않은 코드의 캐치를 런타임으로 지연시키는 것을 보여줍니다. 따라서 원시 유형을 사용하지 않아야합니다.

Type Erasure 섹션에는 Java 컴파일러가 원시 유형을 사용하는 방법에 대한 자세한 정보가 있습니다.

확인되지 않은 오류 메시지

앞에서 언급했듯이 레거시 코드와 일반 코드를 혼합 할 때 다음과 유사한 경고 메시지가 나타날 수 있습니다.

참고 : Example.java는 검사되지 않거나 안전하지 않은 작업을 사용합니다.

참고 : 자세한 내용은 -Xlint : unchecked를 사용하여 다시 컴파일하십시오.

이는 다음 예제와 같이 원시 유형에서 작동하는 이전 API를 사용할 때 발생할 수 있습니다.

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

"체크되지 않은"이라는 용어는 컴파일러에 형식 안전성을 보장하는 데 필요한 모든 형식 검사를 수행하기에 충분한 형식 정보가 없음을 의미합니다. 컴파일러가 힌트를 제공하지만 "확인되지 않은"경고는 기본적으로 비활성화되어 있습니다. 모든 "확인되지 않은"경고를 보려면 -Xlint : unchecked로 다시 컴파일하십시오.

-Xlint : unchecked를 사용하여 이전 예제를 다시 컴파일하면 다음과 같은 추가 정보가 표시됩니다.

WarningDemo.java:4: warning: [unchecked] unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning

확인되지 않은 경고를 완전히 비활성화하려면 -Xlint : -unchecked 플래그를 사용하십시오. @SuppressWarnings("unchecked")주석은 확인 경고를 억제한다. @SuppressWarnings구문에 익숙하지 않은 경우 주석을 참조하십시오.

원본 출처 : Java Tutorials


21

Java에서 "원시"유형은 제네릭이 아니며 유형 안전 일반 유형 매개 변수가 아닌 "원시"오브젝트를 처리하는 클래스입니다.

예를 들어, Java 제네릭을 사용하기 전에 다음과 같은 컬렉션 클래스를 사용합니다.

LinkedList list = new LinkedList();
list.add(new MyObject());
MyObject myObject = (MyObject)list.get(0);

객체를 목록에 추가 할 때는 객체의 유형이 중요하지 않으며 목록에서 객체를 가져 오면 예상 한 유형으로 명시 적으로 캐스팅해야합니다.

제네릭을 사용하면 목록에 들어갈 수있는 개체 유형을 명시 적으로 지정해야하기 때문에 "알 수없는"요소를 제거합니다.

LinkedList<MyObject> list = new LinkedList<MyObject>();
list.add(new MyObject());
MyObject myObject = list.get(0);

제네릭을 사용하면 get 호출에서 오는 객체를 캐스트 할 필요가 없으며 컬렉션은 MyObject에서만 작동하도록 미리 정의되어 있습니다. 이 사실은 제네릭의 주요 동인입니다. 런타임 오류 소스를 컴파일 타임에 확인할 수있는 것으로 변경합니다.


3
보다 구체적으로, 원시 유형은 단순히 일반 유형의 유형 매개 변수를 생략하면 얻을 수있는 유형입니다. 원시 유형은 실제로 이전 버전과의 호환성 기능 일 뿐이며 제거 가능성이 있습니다. ?를 사용하여 비슷한 동작을 얻을 수 있습니다. 와일드 카드 매개 변수.
John Flatness

@ zerocrates : 비슷하지만 다릅니다! ?여전히 사용하면 유형 안전이 제공됩니다. 나는 그것을 내 대답으로 덮었다.
polygenelubricants

19
 private static List<String> list = new ArrayList<String>();

type-parameter를 지정해야합니다.

경고는 제네릭 을 지원하도록 정의 된 형식은 원시 형식을 사용하지 않고 매개 변수화해야합니다.

List제네릭을 지원하도록 정의되었습니다 public class List<E>. 이를 통해 컴파일시 확인되는 많은 유형 안전 작업이 가능합니다.


3
이제 Java 7에서 다이아몬드 유추 로 대체되었습니다. –private static List<String> list = new ArrayList<>();
Ian Campbell

14

원시 유형은 무엇이며 왜 새 코드에서 사용해서는 안된다는 말을 자주 듣습니까?

"원시 유형"은 매개 변수화 된 유형에 유형 인수를 지정하지 않고 일반 클래스를 사용하는 List것입니다 ( 예 : 대신 사용) List<String>. 제네릭이 Java에 도입되었을 때 제네릭을 사용하도록 여러 클래스가 업데이트되었습니다. 이 클래스를 "원시 유형"(유형 인수를 지정하지 않고)으로 사용하면 레거시 코드가 여전히 컴파일 될 수있었습니다.

"원시 유형"은 이전 버전과의 호환성을 위해 사용됩니다. 형식 인수와 함께 제네릭 클래스를 사용하면 더 강력한 타이핑이 가능 해져 코드 이해도를 향상시키고 잠재적 인 문제를 조기에 발견 할 수 있으므로 새 코드에서는 사용하지 않는 것이 좋습니다.

원시 유형을 사용할 수없는 경우 대안은 무엇이며 어떻게 더 좋습니까?

선호되는 대안은 적절한 형식 인수 (예 :)를 사용하여 일반 클래스를 의도 한대로 사용하는 것 List<String>입니다. 이를 통해 프로그래머는 유형을보다 구체적으로 지정할 수 있으며 변수 또는 데이터 구조의 의도 된 사용에 대해 미래의 관리자에게 더 많은 의미를 전달하고 컴파일러가 더 나은 유형 안전을 시행 할 수 있습니다. 이러한 장점을 함께 사용하면 코드 품질이 향상되고 일부 코딩 오류가 발생하는 것을 방지 할 수 있습니다.

예를 들어, 프로그래머가 'names'라는 List 변수에 문자열 만 포함 시키도록하려는 방법의 경우 :

List<String> names = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error

1
아, 그래서 stackoverflow.com/questions/2770111/…polygenelubricants 의 '원시 유형'참조 를 내 자신의 답변 으로 복사하려고 유혹 했지만, 자신의 답변에 사용하도록 남겨 두겠다고 가정합니다.
Bert F

1
예, 본질적으로 사람들이 스택 오버 플로우에서 원시 유형을 사용하는 모든 지역에서 해당 세그먼트를 복사하여 붙여 넣었으며 마침내 이제부터 하나의 질문 만하기로 결정했습니다. 나는 그것이 지역 사회에 좋은 기여가 되길 바랍니다.
polygenelubricants

1
@polygenelubricants 내가 알아 차렸다 – 우리는 같은 질문에 부딪쳤다 :-)
Bert F

1
@ ha9u63ar : 그렇습니다. 일반적으로 간결하고 간단한 답변은 길고 받아 들여지는 답변만큼 좋습니다.
displayName

"강한 syping"이란 무엇입니까?
carloswm85

12

컴파일러는 다음과 같이 작성하기를 원합니다.

private static List<String> list = new ArrayList<String>();

그렇지 않으면에 원하는 유형을 추가 list하여 인스턴스화를 new ArrayList<String>()무의미 하게 만들 수 있기 때문 입니다. Java 제네릭은 컴파일 타임 기능 일 뿐이므로 "원시 유형"의 참조에 할당 된 경우 생성 된 객체 new ArrayList<String>()는 행복하게 받아 들여 Integer지거나 JFrame요소를 받아들입니다 List. 객체 자체는 포함해야하는 유형에 대해 아무것도 모르지만 컴파일러 만 수행합니다.


12

여기서 나는 당신이 개념을 명확하게 할 수있는 여러 가지 경우를 고려하고 있습니다.

1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();

사례 1

ArrayList<String> arrArrayList유형 StringArralyList객체 를 참조하는 유형 의 참조 변수입니다.String . String 유형의 Object 만 보유 할 수 있다는 의미입니다.

StringRaw Type 이 아닌 것은 엄격 하므로 경고가 발생하지 않습니다.

    arr.add("hello");// alone statement will compile successfully and no warning.

    arr.add(23);  //prone to compile time error.
     //error: no suitable method found for add(int)

사례 2

이 경우 ArrayList<String> arr엄격한 유형이지만 Object new ArrayList();는 원시 유형입니다.

    arr.add("hello"); //alone this compile but raise the warning.
    arr.add(23);  //again prone to compile time error.
    //error: no suitable method found for add(int)

다음 arr은 엄격한 유형입니다. 따라서를 추가하면 컴파일 시간 오류가 발생합니다 integer.

경고 :- Raw유형 개체는 Strict유형 참조 변수의를 참조 ArrayList합니다.

사례 3

이 경우 ArrayList arr원시 유형이지만 Object new ArrayList<String>();는 Strict 유형입니다.

    arr.add("hello");  
    arr.add(23);  //compiles fine but raise the warning.

arrRaw Type 이므로 모든 유형의 Object를 추가합니다 .

경고 :- Strict유형 객체는 raw유형 참조 변수를 참조합니다.


8

원시 타입은의는 A의 부족 유형 매개 변수 제네릭 형식을 사용하는 경우.

그것은 삽입과 같은 런타임 오류가 발생할 수 있기 때문에 원시 형은 사용할 수 없습니다 double있어야 있었는지에 Setint들.

Set set = new HashSet();
set.add(3.45); //ok

에서 물건을 검색 할 때 나오는 내용을 Set모릅니다. 이제 당신이 모든 것으로 예상한다고 가정하자 int, 당신이 그것을 캐스팅의 Integer; double3.45가 나오면 런타임에 예외가 발생합니다 .

유형 매개 변수를 추가하면 Set한 번에 컴파일 오류가 발생합니다. 이 선점 오류를 통해 런타임 중에 문제가 발생하기 전에 문제를 해결할 수 있습니다 (따라서 시간과 노력을 절약).

Set<Integer> set = new HashSet<Integer>();
set.add(3.45); //NOT ok.

7

다음은 원시 유형이 물린 경우입니다.

public class StrangeClass<T> {
  @SuppressWarnings("unchecked")
  public <X> X getSomethingElse() {
    return (X)"Testing something else!";
  }

  public static void main(String[] args) {
    final StrangeClass<String> withGeneric    = new StrangeClass<>();
    final StrangeClass         withoutGeneric = new StrangeClass();
    final String               value1,
                               value2;

    // Compiles
    value1 = withGeneric.getSomethingElse();

    // Produces compile error:
    // incompatible types: java.lang.Object cannot be converted to java.lang.String
    value2 = withoutGeneric.getSomethingElse();
  }
}

허용 된 답변에서 언급했듯이 원시 유형의 코드 내에서 제네릭에 대한 모든 지원이 손실됩니다. 모든 유형 매개 변수는 삭제로 변환됩니다 (위의 예에서는 그냥 Object).


5

말하는 것은 당신 list이 지정되지 List않은 물건이라는 것입니다. 즉, Java는 목록에 어떤 종류의 객체가 있는지 알지 못합니다. 그런 다음 목록을 반복하려면 모든 요소를 ​​캐스팅하고 해당 요소의 속성 (이 경우 String)에 액세스 할 수 있어야합니다.

일반적으로 컬렉션을 매개 변수화하는 것이 더 좋습니다. 따라서 변환 문제가 없으며 매개 변수화 된 유형의 요소 만 추가 할 수 있으며 편집기에서 선택할 적절한 방법을 제공합니다.

private static List<String> list = new ArrayList<String>();

4

튜토리얼 페이지 .

원시 유형은 유형 인수가없는 일반 클래스 또는 인터페이스의 이름입니다. 예를 들어, 일반 Box 클래스는 다음과 같습니다.

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

매개 변수화 된 유형의 Box를 작성하려면 공식 유형 매개 변수 T에 실제 유형 인수를 제공하십시오.

Box<Integer> intBox = new Box<>();

실제 유형 인수가 생략되면 원시 유형의 Box를 작성합니다.

Box rawBox = new Box();

2

원시 유형을 피하십시오

원시 유형은 유형 매개 변수를 지정하지 않고 일반 유형을 사용하는 것을 말합니다.

예를 들어 ,

목록은 원시 유형이고 List<String>매개 변수화 된 유형입니다.

JDK 1.5에서 제네릭이 도입되었을 때, 이전 버전의 Java와의 호환성을 유지하기 위해 원시 유형 만 유지되었습니다. 원시 유형을 사용하는 것이 여전히 가능하지만

그들은 피해야합니다 :

  • 그들은 일반적으로 캐스트가 필요합니다
  • 형식이 안전하지 않으며 일부 중요한 종류의 오류는 런타임에만 나타납니다.
  • 그들은 덜 표현하고, 매개 변수 유형과 같은 방식으로 자기 문서를하지 않는

    import java.util.*;
    
    public final class AvoidRawTypes {
    
    void withRawType() {
    
        //Raw List doesn't self-document, 
        //doesn't state explicitly what it can contain
    
        List stars = Arrays.asList("Arcturus", "Vega", "Altair");
    
        Iterator iter = stars.iterator();
    
        while (iter.hasNext()) {
    
            String star = (String) iter.next(); //cast needed
    
            log(star);
        }
    
    }
    
    void withParameterizedType() {
    
        List < String > stars = Arrays.asList("Spica", "Regulus", "Antares");
    
        for (String star: stars) {
    
            log(star);
        }
    
    }
    
    private void log(Object message) {
    
        System.out.println(Objects.toString(message));
    
    }
    
    }

참조 : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html


1

몇 가지 샘플 연습을 수행하고 똑같은 퍼즐을 한 후에이 페이지를 찾았습니다.

============== 샘플에서 제공 한대로이 코드에서 나왔습니다 ================

public static void main(String[] args) throws IOException {

    Map wordMap = new HashMap();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) {
        Map.Entry entry = (Map.Entry) i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

======================이 코드에 =========================

public static void main(String[] args) throws IOException {
    // replace with TreeMap to get them sorted by name
    Map<String, Integer> wordMap = new HashMap<String, Integer>();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator<Entry<String, Integer>> i = wordMap.entrySet().iterator(); i.hasNext();) {
        Entry<String, Integer> entry =   i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

}

===================================================== ===============================

더 안전 할 수는 있지만 철학을 어지럽히는 데 4 시간이 걸렸습니다 ...


0

표현하려는 내용을 표현할 때 원시 유형이 좋습니다.

예를 들어, 역 직렬화 함수는을 반환 List하지만 목록의 요소 유형을 모릅니다. 그래서 List여기에 적절한 반환 형식이다.


당신이 사용할 수있는 ? 유형 매개 변수로
Dániel Kis

그렇습니다, 그러나 그것은 타이핑하는 것이 더 많고 더 타이핑하는 것에 반대합니다. :)
Stefan Reich
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.