확인되지 않은 캐스트 경고는 어떻게 해결합니까?
Eclipse는 다음과 같은 경고를 표시합니다.
형식 안전성 : 개체에서 HashMap으로 확인되지 않은 캐스트
이것은 Object를 반환하는 제어 할 수없는 API에 대한 호출입니다.
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
이론적으로 적어도 잠재적 인 코드 문제를 나타 내기 때문에 가능한 경우 Eclipse 경고를 피하고 싶습니다. 그래도 아직이 문제를 제거하는 좋은 방법을 찾지 못했습니다. 메서드 자체에 포함 된 한 줄을 추출하여 @SuppressWarnings("unchecked")
해당 메서드에 추가 할 수 있으므로 경고를 무시하는 코드 블록의 영향을 제한 할 수 있습니다. 더 나은 옵션이 있습니까? Eclipse에서 이러한 경고를 끄고 싶지 않습니다.
코드를보기 전에는 더 간단했지만 여전히 경고가 발생했습니다.
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
경고가 표시되는 해시를 사용하려고 할 때 다른 곳에서 문제가 발생했습니다.
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
물론 확실한 대답은 체크되지 않은 캐스트를하지 않는 것입니다.
꼭 필요한 경우 적어도 @SuppressWarnings
주석 의 범위를 제한하십시오 . Javadocs 에 따르면 지역 변수로 이동할 수 있습니다. 이렇게하면 전체 방법에 영향을주지 않습니다.
예:
@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();
Map
실제로 일반 매개 변수를 가져야 하는지 여부를 판별 할 방법이 없습니다 <String, String>
. 매개 변수가 무엇인지 미리 알아야합니다 (또는를 얻을 때 알 수 있습니다 ClassCastException
). 이것이 코드가 경고를 생성하는 이유입니다. 컴파일러가 안전한지 여부를 알 수 없기 때문입니다.
불행히도 여기에는 훌륭한 옵션이 없습니다. 이 모든 것의 목표는 형식 안전성을 유지하는 것입니다. " Java Generics "는 제네릭 화되지 않은 레거시 라이브러리를 처리하기위한 솔루션을 제공하며, 섹션 8.2에 특히 "빈 루프 기술"이라고하는 것이 있습니다. 기본적으로 안전하지 않은 캐스트를 만들고 경고를 억제하십시오. 그런 다음 다음과 같이 맵을 반복합니다.
@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());
예기치 않은 유형이 발생하면 런타임 ClassCastException
이 발생하지만 적어도 문제의 원인에 가깝게 발생합니다.
와; 내 질문에 대한 답을 알아 낸 것 같습니다. 그럴만 한 가치가 있는지 모르겠어요! :)
문제는 캐스트가 확인되지 않는다는 것입니다. 따라서 직접 확인해야합니다. 매개 변수화 된 유형 정보는 런타임에 사용할 수없고 컴파일 시간에 지워졌 기 때문에 instanceof로 매개 변수화 된 유형을 확인할 수 없습니다.
그러나 instanceof를 사용하여 해시의 모든 항목에 대해 검사를 수행 할 수 있으며 그렇게함으로써 유형이 안전한 새 해시를 생성 할 수 있습니다. 그리고 당신은 어떤 경고도 일으키지 않을 것입니다.
mmyers와 Esko Luontola 덕분에 여기에서 처음 작성한 코드를 매개 변수화하여 어딘가의 유틸리티 클래스에 래핑하고 매개 변수화 된 HashMap에 사용할 수 있습니다. 더 잘 이해하고 싶고 제네릭에 익숙하지 않은 경우이 답변의 편집 기록을 보는 것이 좋습니다.
public static <K, V> HashMap<K, V> castHash(HashMap input,
Class<K> keyClass,
Class<V> valueClass) {
HashMap<K, V> output = new HashMap<K, V>();
if (input == null)
return output;
for (Object key: input.keySet().toArray()) {
if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
Object value = input.get(key);
if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
K k = keyClass.cast(key);
V v = valueClass.cast(value);
output.put(k, v);
} else {
throw new AssertionError(
"Cannot cast to HashMap<"+ keyClass.getSimpleName()
+", "+ valueClass.getSimpleName() +">"
+", value "+ value +" is not a "+ valueClass.getSimpleName()
);
}
} else {
throw new AssertionError(
"Cannot cast to HashMap<"+ keyClass.getSimpleName()
+", "+ valueClass.getSimpleName() +">"
+", key "+ key +" is not a " + keyClass.getSimpleName()
);
}
}
return output;
}
그것은 많은 일이고, 아마도 아주 적은 보상으로 ... 나는 그것을 사용할지 말지 모르겠습니다. 사람들이 그만한 가치가 있다고 생각하는지에 대한 의견을 부탁드립니다. 또한 개선 제안에 감사드립니다. AssertionErrors를 던지는 것 외에 더 나은 방법이 있습니까? 내가 던질 수있는 더 좋은 것이 있습니까? 확인 된 예외로 만들어야합니까?
Eclipse 환경 설정에서 Java-> Compiler-> Errors / Warnings-> Generic 유형으로 이동하여 확인란을 선택하십시오 Ignore unavoidable generic type problems
.
이것은 질문의 의도를 충족시킵니다.
Eclipse 경고를 피하고 싶습니다 ...
정신이 아니라면.
다음과 같은 유틸리티 클래스를 만들고이를 사용하여 확인되지 않은 경고를 억제 할 수 있습니다.
public class Objects {
/**
* Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
*/
@SuppressWarnings({"unchecked"})
public static <T> T uncheckedCast(Object obj) {
return (T) obj;
}
}
다음과 같이 사용할 수 있습니다.
import static Objects.uncheckedCast;
...
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
return uncheckedCast(session.getAttribute("attributeKey"));
}
이에 대한 더 많은 논의는 여기에 있습니다 : http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
이 물건은 어렵지만 현재 내 생각은 다음과 같습니다.
API가 Object를 반환하면 수행 할 수있는 작업이 없습니다. 무슨 일이 있어도 맹목적으로 객체를 캐스팅하게됩니다. Java가 ClassCastExceptions를 발생 시키거나 각 요소를 직접 확인하고 Assertions 또는 IllegalArgumentExceptions 등을 발생시킬 수 있지만 이러한 런타임 검사는 모두 동일합니다. 런타임에 수행하는 작업에 관계없이 컴파일 시간 확인되지 않은 캐스트 를 억제 해야합니다.
나는 블라인드 캐스트를 선호하고, 우리는 API가 무엇을 반환해야하는지 "알고"일반적으로 API가 작동한다고 가정하기 때문에 JVM이 저를 위해 런타임 검사를 수행하도록합니다. 필요한 경우 캐스트 위의 모든 곳에서 제네릭을 사용하십시오. 여전히 단일 블라인드 캐스트를 가지고 있기 때문에 실제로 아무것도 구입하지 않지만 적어도 거기에서 제네릭을 사용할 수 있으므로 JVM이 다른 코드 조각에서 블라인드 캐스트를 피할 수 있도록 도와줍니다.
이 특정 경우에 아마도 SetAttribute에 대한 호출을 볼 수 있고 형식이 들어오는 것을 볼 수 있으므로 나가는 동안 형식을 동일하게 블라인드 캐스팅하는 것은 부도덕하지 않습니다. SetAttribute를 참조하는 주석을 추가하고이를 수행하십시오.
HTTP 세션 세계에서는 API가 그렇게 작성 되었기 때문에 실제로 캐스트를 피할 수 없습니다 Object
.
약간의 작업만으로도 체크되지 않은 캐스트를 쉽게 피할 수 있습니다. 즉 ClassCastException
, 오류가 발생할 경우 오른쪽을 제공하는 전통적인 캐스트로 바뀝니다 .) 확인되지 않은 예외 CCE
는 캐스트 지점 대신 나중에 언제든지 로 바뀔 수 있습니다 (이것이 별도의 경고 인 이유입니다).
HashMap을 전용 클래스로 바꿉니다.
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Attributes extends AbstractMap<String, String> {
final Map<String, String> content = new HashMap<String, String>();
@Override
public Set<Map.Entry<String, String>> entrySet() {
return content.entrySet();
}
@Override
public Set<String> keySet() {
return content.keySet();
}
@Override
public Collection<String> values() {
return content.values();
}
@Override
public String put(final String key, final String value) {
return content.put(key, value);
}
}
그런 다음 대신 해당 클래스로 캐스팅하면 Map<String,String>
코드를 작성한 정확한 위치에서 모든 것이 확인됩니다. ClassCastExceptions
나중에 예상치 못한 일이 없습니다 .
다음은 다른 답변에서 언급 된 두 가지 전략을 사용 하여 "확인되지 않은 캐스트"경고를 피하는 단축 된 예입니다 .
관심 유형의 클래스를 런타임시 매개 변수로 전달합니다 (
Class<T> inputElementClazz
). 그런 다음 다음을 사용할 수 있습니다.inputElementClazz.cast(anyObject);
컬렉션의 유형 캐스팅에는 와일드 카드? 일반 유형 T 대신 레거시 코드 (
Collection<?> unknownTypeCollection
) 에서 어떤 종류의 객체를 기대해야하는지 실제로 알 수 없음을 인식합니다 . 결국, 이것은 "확인되지 않은 캐스트"경고가 우리에게 말하고자하는 것입니다. 우리는 우리가를 얻었는지 확신 할 수 없습니다. 따라서해야 할Collection<T>
정직한 일은Collection<?>
. 꼭 필요한 경우 알려진 유형의 컬렉션을 빌드 할 수 있습니다 (Collection<T> knownTypeCollection
).
아래 예에서 인터페이스 된 레거시 코드는 StructuredViewer에 "input"속성을 가지고 있습니다 (StructuredViewer는 트리 또는 테이블 위젯이고 "input"은 그 뒤에있는 데이터 모델입니다). 이 "입력"은 모든 종류의 Java 컬렉션이 될 수 있습니다.
public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
// legacy code returns an Object from getFirstElement,
// the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
T firstElement = inputElementClazz.cast(selection.getFirstElement());
// legacy code returns an object from getInput, so we deal with it as a Collection<?>
Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();
// for some operations we do not even need a collection with known types
unknownTypeCollection.remove(firstElement);
// nothing prevents us from building a Collection of a known type, should we really need one
Collection<T> knownTypeCollection = new ArrayList<T>();
for (Object object : unknownTypeCollection) {
T aT = inputElementClazz.cast(object);
knownTypeCollection.add(aT);
System.out.println(aT.getClass());
}
structuredViewer.refresh();
}
당연히 위의 코드는 잘못된 데이터 유형이있는 레거시 코드를 사용하는 경우 (예 : Java 컬렉션 대신 StructuredViewer의 "입력"으로 배열을 설정 한 경우) 런타임 오류를 일으킬 수 있습니다.
메서드 호출의 예 :
dragFinishedStrategy.dragFinished(viewer, Product.class);
이 특별한 경우에는 Maps를 HttpSession에 직접 저장하지 않고 대신 Map (클래스의 구현 세부 사항)을 포함하는 내 클래스의 인스턴스를 저장합니다. 그런 다음지도의 요소 유형이 올바른지 확인할 수 있습니다.
그러나 어쨌든지도의 내용이 올바른 유형인지 확인하려면 다음과 같은 코드를 사용할 수 있습니다.
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("b", 2);
Object obj = map;
Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
Map<String, String> error = safeCastMap(obj, String.class, String.class);
}
@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
checkMap(map);
checkMapContents(keyType, valueType, (Map<?, ?>) map);
return (Map<K, V>) map;
}
private static void checkMap(Object map) {
checkType(Map.class, map);
}
private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
checkType(keyType, entry.getKey());
checkType(valueType, entry.getValue());
}
}
private static <K> void checkType(Class<K> expectedType, Object obj) {
if (!expectedType.isInstance(obj)) {
throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
}
}
Android Studio에서 검사를 비활성화하려면 다음을 사용할 수 있습니다.
//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();
Esko Luontola의 위 답변에서 Objects.Unchecked 유틸리티 기능은 프로그램 혼란을 피하는 좋은 방법입니다.
전체 메서드에서 SuppressWarnings를 원하지 않는 경우 Java는이를 로컬에 배치하도록합니다. 멤버에 대한 캐스트가 필요한 경우 다음과 같은 코드로 이어질 수 있습니다.
@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;
유틸리티를 사용하는 것이 훨씬 더 깨끗하고 무엇을하는지 분명합니다.
this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());
참고 : 때때로 경고가 실제로 다음과 같이 잘못하고 있음을 의미한다고 덧붙이는 것이 중요하다고 생각합니다.
ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList;
// this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here
컴파일러가 말하는 것은이 캐스트가 런타임에 확인되지 않으므로 일반 컨테이너의 데이터에 액세스 할 때까지 런타임 오류가 발생하지 않는다는 것입니다.
경고 억제는 해결책이 아닙니다. 하나의 문에서 2 단계 캐스팅을 수행해서는 안됩니다.
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
// first, cast the returned Object to generic HashMap<?,?>
HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");
// next, cast every entry of the HashMap to the required type <String, String>
HashMap<String, String> returingHash = new HashMap<>();
for (Entry<?, ?> entry : theHash.entrySet()) {
returingHash.put((String) entry.getKey(), (String) entry.getValue());
}
return returingHash;
}
코드를 게시하면 빠른 추측으로 확실하게 말할 수 있지만
HashMap<String, Object> test = new HashMap();
필요한 경우 경고를 생성합니다.
HashMap<String, Object> test = new HashMap<String, Object>();
볼만한 가치가 있을지도 몰라
해야 할 일에 익숙하지 않은 경우.
질문을 오해했을 수도 있지만 (예제와 몇 개의 주변 줄이 좋을 것입니다), 왜 항상 적절한 인터페이스 (및 Java5 +)를 사용하지 않습니까? HashMap
대신에 캐스트하고 싶은 이유가 없습니다 Map<KeyType,ValueType>
. 사실, 내가 상상할 수 있는 에 변수의 유형을 설정하는 이유 HashMap
대신을 Map
.
그리고 왜 소스는 Object
? 레거시 컬렉션의 매개 변수 유형입니까? 그렇다면 제네릭을 사용하고 원하는 유형을 지정하십시오.
Generics를 지원하지 않는 API를 사용해야하는 경우 .. 가능한 한 적은 줄로 래퍼 루틴에서 이러한 호출을 분리하려고합니다. 그런 다음 SuppressWarnings 주석을 사용하고 동시에 형식 안전성 캐스트를 추가합니다.
이것은 가능한 한 깔끔하게 유지하려는 개인적인 취향입니다.
이걸 가져 가면 새 HashMap을 만드는 것보다 훨씬 빠릅니다. 이미 하나이지만 각 요소가 해당 유형에 대해 검사되므로 여전히 안전합니다.
@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
assert input instanceof Map : input;
for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
}
if (input instanceof HashMap)
return (HashMap<K, V>) input;
return new HashMap<K, V>((Map<K, V>) input);
}
컴퓨터 과학의 거의 모든 문제는 간접적 수준 * 또는 무언가를 추가하여 해결할 수 있습니다.
따라서 .NET Framework보다 높은 수준의 비 제네릭 개체를 도입하십시오 Map
. 컨텍스트가 없으면 그다지 설득력이 없어 보이지만 어쨌든 :
public final class Items implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Map<String,String> map;
public Items(Map<String,String> map) {
this.map = New.immutableMap(map);
}
public Map<String,String> getMap() {
return map;
}
@Override public String toString() {
return map.toString();
}
}
public final class New {
public static <K,V> Map<K,V> immutableMap(
Map<? extends K, ? extends V> original
) {
// ... optimise as you wish...
return Collections.unmodifiableMap(
new HashMap<String,String>(original)
);
}
}
static Map<String, String> getItems(HttpSession session) {
Items items = (Items)
session.getAttribute("attributeKey");
return items.getMap();
}
* 너무 많은 수준의 간접 참조를 제외하고.
equals()
작업을 재정의 할 때이를 처리하는 한 가지 방법이 있습니다.
public abstract class Section<T extends Section> extends Element<Section<T>> {
Object attr1;
/**
* Compare one section object to another.
*
* @param obj the object being compared with this section object
* @return true if this section and the other section are of the same
* sub-class of section and their component fields are the same, false
* otherwise
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
// this exists, but obj doesn't, so they can't be equal!
return false;
}
// prepare to cast...
Section<?> other;
if (getClass() != obj.getClass()) {
// looks like we're comparing apples to oranges
return false;
} else {
// it must be safe to make that cast!
other = (Section<?>) obj;
}
// and then I compare attributes between this and other
return this.attr1.equals(other.attr1);
}
}
This seems to work in Java 8 (even compiled with -Xlint:unchecked
)
Just typecheck it before you cast it.
Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;
And for anyone asking, it's quite common to receive objects where you aren't sure of the type. Plenty of legacy "SOA" implementations pass around various objects that you shouldn't always trust. (The horrors!)
EDIT Changed the example code once to match the poster's updates, and following some comments I see that instanceof doesn't play nicely with generics. However changing the check to validate the outer object seems to play well with the commandline compiler. Revised example now posted.
If you are sure that the type returned by session.getAttribute() is HashMap then you can not typecast to that exact type, but rely on only checking the generic HashMap
HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {
HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
return theHash;
}
Eclipse will then surprise warnings, but of course this can lead to runtime errors that can be hard to debug. I use this approach in not operation-critical contexts only.
Two ways, one which avoids the tag completely, the other using a naughty but nice utility method.
The problem is pre-genericised Collections...
I believe the rule of thumb is: "cast objects one thing at a time" - what this means when trying to use raw classes in a genericised world is that because you don't know what is in this Map<?, ?> (and indeed the JVM might even find that it isn't even a Map!), it obvious when you think about it that you can't cast it. If you had a Map<String, ?> map2 then HashSet<String> keys = (HashSet<String>)map2.keySet() does not give you a warning, despite this being an "act of faith" for the compiler (because it might turn out to be a TreeSet)... but it is only a single act of faith.
PS to the objection that iterating as in my first way "is boring" and "takes time", the answer is "no pain no gain": a genericised collection is guaranteed to contain Map.Entry<String, String>s, and nothing else. You have to pay for this guarantee. When using generics systematically this payment, beautifully, takes the form of coding compliance, not machine time!
One school of thought might say that you should set Eclipse's settings to make such unchecked casts errors, rather than warnings. In that case you would have to use my first way.
package scratchpad;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
public class YellowMouse {
// First way
Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");
Map<String, String> yellowMouse = new HashMap<String, String>();
for( Map.Entry<?, ?> entry : theHash.entrySet() ){
yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
}
return yellowMouse;
}
// Second way
Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
return uncheckedCast( session.getAttribute("attributeKey") );
}
// NB this is a utility method which should be kept in your utility library. If you do that it will
// be the *only* time in your entire life that you will have to use this particular tag!!
@SuppressWarnings({ "unchecked" })
public static synchronized <T> T uncheckedCast(Object obj) {
return (T) obj;
}
}
This makes the warnings go away...
static Map<String, String> getItems(HttpSession session) {
HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
return theHash;
}
Solution: Disable this warning in Eclipse. Don't @SuppressWarnings it, just disable it completely.
Several of the "solutions" presented above are way out of line, making code unreadable for the sake of suppressing a silly warning.
참고URL : https://stackoverflow.com/questions/509076/how-do-i-address-unchecked-cast-warnings
'development' 카테고리의 다른 글
HTML 파일에 다른 HTML 파일 포함 (0) | 2020.10.03 |
---|---|
JavaScript에서 문자열을 날짜로 변환 (0) | 2020.10.03 |
한 AngularJS 컨트롤러가 다른 컨트롤러를 호출 할 수 있습니까? (0) | 2020.10.03 |
Ruby on Rails 서버 옵션 (0) | 2020.10.03 |
ViewPager PagerAdapter가보기를 업데이트하지 않음 (0) | 2020.10.03 |