유형 안전 : 검사되지 않은 캐스트


265

내 봄 응용 프로그램 컨텍스트 파일에는 다음과 같은 것이 있습니다.

<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
    <entry key="some_key" value="some value" />
    <entry key="some_key_2" value="some value" />   
</util:map>

Java 클래스에서 구현은 다음과 같습니다.

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

이클립스에서는 다음과 같은 경고가 표시됩니다.

유형 안전 : Object에서 HashMap으로 캐스트되지 않은 캐스트

내가 뭘 잘못 했어? 문제를 어떻게 해결합니까?


실제로 선택하지 않은 캐스트 경고를 제거 파라미터의 HashMap에 캐스팅 확인에 나는 일상 내놓았다 : 링크 나는 이것이 "올바른"솔루션입니다라고 말하고 싶지만, 그러나 그것의 가치가 있는지 여부는 논쟁의 여지가있을 수 있습니다. :)
skiphoppy


답변:


248

글쎄, 우선, 당신은 새로운 HashMap창조 호출로 기억을 낭비하고 있습니다. 두 번째 줄은 생성 된이 해시 맵에 대한 참조를 완전히 무시하여 가비지 수집기에서 사용할 수있게합니다. 따라서 그렇게하지 마십시오.

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

둘째, 컴파일러는 객체가 HashMap검사인지 여부를 확인하지 않고 객체를 캐스팅한다고 불평합니다 HashMap. 그러나 당신이해야 할 경우에도 :

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

여전히이 경고가 표시 될 것입니다. 문제는 getBeanreturns Object이므로 유형이 무엇인지 알 수 없습니다. 그것을 HashMap직접 변환해도 두 번째 경우에는 문제가 발생하지 않을 것입니다 (첫 번째 경우에는 경고가 없을 것입니다 .Java 컴파일러에 Java 5에 대한 경고가 얼마나 실용적인지 잘 모르겠습니다). 그러나 당신은 그것을로 변환하고 HashMap<String, String>있습니다.

HashMaps는 실제로 객체를 키로 사용하고 객체를 값으로 갖는 맵 HashMap<Object, Object>입니다. 따라서 Bean을 얻을 때 리턴되는 비 제네릭 표현에 오브젝트가 HashMap<String, String>있을 수 있기 때문에 Bean으로 표시 될 수 있다는 보장이 없습니다 HashMap<Date, Calendar>.

코드가 컴파일되고 String value = map.get("thisString");오류없이 실행할 수 있으면이 경고에 대해 걱정하지 마십시오. 그러나 맵이 문자열 키에 대한 문자열 키로 완전히 구성되지 않은 경우, ClassCastException제네릭이이 경우 발생하는 것을 막을 수 없기 때문에 런타임에 얻을 수 있습니다.


12
이것은 얼마 전이지만 캐스팅 전에 Set <CustomClass>를 확인하는 유형에 대한 답변을 찾고 있었고 매개 변수화 된 제네릭에서는 인스턴스를 만들 수 없습니다. 예를 들어 if (event.getTarget instanceof Set <CustomClass>)?만으로 제네릭을 입력 할 수 있습니다. 캐스트 경고를 제거하지 않습니다. 예 : if (event.getTarget instanceof Set <?>)
garlicman

315

문제는 캐스트 런타임 검사입니다 -하지만 형의 삭제로 인해 런타임에 실제로 사이에는 차이가 없습니다 HashMap<String,String>HashMap<Foo,Bar>기타에 FooBar.

사용 @SuppressWarnings("unchecked")하고 코를 누르고 있습니다. 아, 그리고 자바의 제네릭에 대한 캠페인 :)


14
나는 타입이 지정되지 않은 NSMutableWhatever에 대해 Java의 통일 된 제네릭을 사용할 것입니다. 적어도 자바는 노력하고 있습니다.
Dan Rosenstark

12
바로 그거죠. 유형 검사를 고집하는 경우 HashMap <?,?>으로 만 수행 할 수 있으며 일반 유형을 검사하지 않는 것과 동일하므로 경고를 제거하지 않습니다. 세상의 종말은 아니지만 경고를 억제하거나 그와 함께 살고 있다는 것이 귀찮습니다.
garlicman

5
@JonSkeet 통일 된 제네릭이란 무엇입니까?
SasQ

89

위의 메시지에서 알 수 있듯이 목록은 a List<Object>와 a List<String>또는 a로 구별 할 수 없습니다 List<Integer>.

비슷한 문제 로이 오류 메시지를 해결했습니다.

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

다음과 같이 :

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

설명 : 첫 번째 유형 변환은 오브젝트가 보유한 유형을 고려하지 않고 목록인지 확인합니다 (목록 레벨에서 내부 유형을 확인할 수 없기 때문에). 컴파일러는 List에 일종의 객체가 포함되어 있음을 알고 있으므로 두 번째 변환이 필요합니다. 그러면 액세스 할 때 List의 각 개체 유형이 확인됩니다.


3
넌 내 친구 야 목록을 캐스팅하는 대신 목록을 반복하고 각 요소를 캐스팅하면 경고가 표시되지 않습니다.
후안 이사 자

2
이 경고를 제거하지만 여전히 나는 확신하지 않다 : P
mumair

1
예 컴파일러를 blindfolding 같은 느낌하지만 런타임 : D 그래서 나는이와 @SuppressWarnings의 차이 볼 수 없습니다 ( "선택 해제"를)
channae

1
대단해! @SupressWarning을 사용하는 주된 차이점은 주석을 사용하면 IDE 및 코드 분석 도구에서 경고가 제거되지만 -Werror 플래그 컴파일을 사용하는 경우 아직 오류가 발생한다는 것입니다. 이 방법을 사용하면 두 가지 경고가 모두 해결됩니다.
Edu Costa

30

경고는 바로 그것입니다. 경고. 때때로 경고는 관련이 없으며 때로는 그렇지 않습니다. 컴파일러가 문제가 될 수 있지만 그렇지 않을 수도 있다고 생각하는 것에주의를 기울이는 데 사용됩니다.

캐스트의 경우이 경우 항상 경고가 표시됩니다. 특정 캐스트가 안전하다고 확신하는 경우 줄 바로 앞에 다음과 같은 주석을 추가하는 것이 좋습니다 (구문을 잘 모르겠습니다).

@SuppressWarnings (value="unchecked")

14
-1 : 경고를 수락해서는 안됩니다. 또는 이러한 종류의 경고를 억제하거나 수정하십시오. 당신은 많은 경고를해야 할 순간이 올 것이고 당신은 관련을 한 번 보지 못할 것입니다.
ezdazuzena

10
매개 변수가있는 제네릭 (예 : Map)을 캐스팅 할 때는 클래스 캐스팅 경고를 피할 수 없으므로 원래 질문에 가장 적합합니다.
muttonUp

9

getBean이 오브젝트 참조를 리턴하고 올바른 유형으로 캐스트하기 때문에이 메시지가 표시됩니다. Java 1.5는 경고를 표시합니다. 이것이 이와 같은 코드로 Java 1.5 이상을 사용하는 특성입니다. 스프링은 typesafe 버전을 가지고 있습니다

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

할 일 목록에 있습니다.


6

실제로 경고를 제거하려면 일반 클래스에서 확장되는 클래스를 작성해야합니다.

예를 들어, 사용하려는 경우

private Map<String, String> someMap = new HashMap<String, String>();

당신은 같은 새로운 클래스를 만들 수 있습니다

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

그런 다음 사용하면

someMap = (StringMap) getApplicationContext().getBean("someMap");

컴파일러는 (더 이상 일반적인) 유형이 무엇인지 알고 있으며 경고는 없습니다. 이것은 항상 완벽한 해결책은 아니지만 일부는 이런 종류의 제네릭 클래스의 목적을 무효화한다고 주장 할 수도 있지만 제네릭 클래스의 동일한 코드를 모두 재사용하고 있으며 컴파일 타임에 어떤 유형을 선언하고 있습니까? 사용하고 싶습니다.


3

확인되지 않은 경고를 피하기위한 해결책 :

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");

해결책이 아닌 해킹처럼 보입니다.
Malwinder Singh

1
-직렬화 가능 클래스 MyMap은 long 유형의 정적 final serialVersionUID 필드를 선언하지 않습니다. {
Ulterior

1

다른 솔루션은 동일한 객체를 많이 캐스팅하고로 코드 @SupressWarnings("unchecked")를 작성 하지 않으려 는 경우 주석으로 메소드를 작성하는 것입니다. 이렇게하면 캐스트를 중앙 집중화하고 오류 가능성을 줄일 수 있습니다.

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}

1

아래 코드는 타입 안전 경고를 유발합니다

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

해결 방법

목록 내에 보유 된 객체 유형이 확인되지 않았으므로 매개 변수를 언급하지 않고 새지도 객체를 만듭니다.

1 단계 : 새로운 임시지도 만들기

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

2 단계 : 기본 맵 인스턴스화

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

3 단계 : 임시지도를 반복하고 값을 기본지도로 설정

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }

0

내가 뭘 잘못 했어? 문제를 어떻게 해결합니까?

여기 :

Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");

다음과 같이 반환하기 때문에 일반적으로 사용하지 않으려는 레거시 메서드를 사용합니다 Object.

Object getBean(String name) throws BeansException;

빈 팩토리에서 빈을 가져오고 (싱글 톤용) / 만들기 (프로토 타입 용)를 선호하는 방법은 다음과 같습니다.

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

다음과 같이 사용하십시오.

Map<String,String> someMap = app.getBean(Map.class,"someMap");

모든 Map객체가 반드시 Map<String, String>객체 가 아니기 때문에 컴파일되지만 여전히 확인되지 않은 변환 경고가 표시 됩니다.

그러나 <T> T getBean(String name, Class<T> requiredType) throws BeansException;제네릭 컬렉션과 같은 빈 제네릭 클래스에서는 하나 이상의 클래스를 매개 변수로 지정해야하기 때문에 충분하지 않습니다 (컬렉션 유형 및 제네릭 유형).

이런 종류의 시나리오에서 일반적으로 더 나은 방법은 직접 BeanFactory메소드 를 사용하지 않고 프레임 워크가 Bean을 주입하도록하는 것입니다.

빈 선언 :

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

콩 주입 :

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.