2010-07-16 2 views
4

Я получаю предупреждение о неконтролируемых отбрасываниях по значению «return (T)»; линия. Есть ли лучший способ сделать это, или я должен просто подавить предупреждение?generics question

class SomeClass<T> 
{ 
    /* ... other methods ... */ 

    private Set<T> aSet; 
    public T filter(Object value) 
    { 
     if (this.aSet.contains(value)) 
      return (T) value; 
     else 
      return null; 
    } 
} 

редактировать: я застрял с public T filter(Object value) в качестве подписи.

+0

Пожалуйста, измените это так его компилирует на запрос Coronatus. – apollodude217

+0

добавлено , thanks –

ответ

2

Разрабатывая ответ Тома Хотина, вы можете вместо этого использовать Map<T,T>, что касается проблемы кастинга. Если вы используете HashSet<T> для исполнения aSet, HashSet все равно использует HashMap<T,HashSet<T>> (он использует ссылки на себя как значения - не уверен, есть ли причина для этого иного, чем выбор) с помощью «значений» набора ключи) и выполняет основную часть своих операций, просто переинтерпретируя возвращаемые значения функций Map.

Следовательно, вы могли это сделать, если вы хотите (и я не вижу причин, почему сразу же было бы любое менее производительным, чем HashSet):

class SomeClass<T> 
{ 
    /* ... other methods ... */ 

    private Map<T,T> aSet; 

    public T filter(Object value) 
    { 
     // Will return the properly-typed object if it's in 
     // the "set" otherwise will return null 
     return aSet.get(value); 
    } 
} 
+0

Спасибо за дополнительное объяснение Тим – sixtyfootersdude

4

Что касается использования аргумента generic type в качестве типа аргумента для фильтрации.

class SomeClass 
{ 
    /* ... other methods ... */ 

    private Set<T> aSet; 
    public T filter(T value) 
    { 
     if (this.aSet.contains(value)) 
      return (T) value; 
     else 
      return null; 
    } 
} 

Хорошо, так как вы застряли с object подписи, у вас нет другого шанса но отключить/игнорировать предупреждение. Java Generics не являются «настоящими генериками» в том смысле, что базовая система типов поддерживает их. Фактически, это всего лишь компилятор, поскольку они основаны на тире Erasure. Пределы производительности и, возможно, небезопасное кастинг - это цена совместимости двоичных файлов с более старыми версиями JVM.

Вы можете сравнить это с .NET CLR, имеющим реальные дженерики, я написал blog post, сравнивая два подхода, которые вы недавно можете использовать, если какое-либо из сказанного выше оставило вас в замешательстве.

+0

Вы забыли пространство между T и значением, и вам не нужно, чтобы приведение к (T) в операторе return. –

+0

да, и я просто хотел отредактировать это, и они были ... просто там ;-) –

+0

Это вообще возможно, но не в моем случае: я использую его с классом, который предшествует дженерикам, поэтому он просто дает мне объект , –

1

Это вы имеете в виду?

public class SomeClass<T> 
{ 
    private Set<T> aSet; 

    public T filter(Object value) 
    { 
     return (aSet.contains(value) ? (T) value : null); 
    } 
} 
+0

да, но почему это было бы лучше? (Я читаю это на другом компьютере, где у меня нет простого доступа к Java IDE) –

+0

Потому что он соответствует, а тройной 'if' является более приятным :) –

+0

это не решает проблему, вы просто изменение синтаксиса. –

3

В этом случае можно подавить предупреждение и оставить комментарий, почему это сохранить:

if (this.aSet.contains(value)) { 
     // this following cast from Object to T is save, because ... 
     @SuppressWarnings("unchecked") T result = (T) value; 
     return result; 
    } else 
     return null; 

Там альтернатива, но это потребовало бы, что класс или метод «знает» это parametized тип. Затем мы можем бросить без предупреждения (но тоже должны комментировать). Я показываю решение, в котором мы ввести/изменить конструктор для хранения информации «дженериков» в качестве поля:

public class SomeClass<T> { 

    private Set<T> aSet; 

    private Class<T> genericType; 
    public SomeClass(Class<T> genericType) { 
     this.genericType = genericType; 
    } 

    public T filter(Object value) 
    { 
     if (this.aSet.contains(value)) 
      // this following cast from Object to T is save, because ... 
      return genericType.cast(value); 
     else 
      return null; 
    } 
} 
+0

Не забывайте, что 'genericType.cast (...)' может вызывать 'ClassCastException'! Конечно, так же может быть и обычный синтаксис каста, но литье «Class» может вызывать исключение только во время выполнения. –

+0

@Brian - вот почему комментарий в предыдущей строке. Но, по крайней мере, исключение ClassCastException, созданное 'cast', довольно многословно. –

2

Это возможно :)

public T filter(Object value) 
{ 
    if (this.aSet.contains(value)) { 
     Set<T> tmp = new TreeSet<T>(aSet); 
     tmp.retainAll(Collections.singleton(value)); 
     return tmp.iterator().next(); 
    } 
    return null; 
} 

, но это, очевидно, уродливее, чем делать бросок.

+0

+1 для интересного. –

0

Проблема с этим заключается в том, что это событие класса ClassCastException. Если у вас есть объект, который не относится к классу T, но есть equals с одним из ваших элементов, когда вы пытаетесь отбросить его на T, он потерпит неудачу. Вам нужно получить фактическое значение, которое содержится в наборе, которое, как вы знаете, имеет тип T. К сожалению, Set не имеет метода get, поэтому вам нужно временно преобразовать его в коллекцию, которая имеет Список.

Вы могли бы попытаться сделать это следующим образом:

private Set<T> aSet; 

public T filter(Object value) { 
    List<T> list = new ArrayList<T>(aSet); 
    int index = list.indexOf(value); 
    if (index == -1) { 
     return null; 
    } 
    T setValue = list.get(index); 
    return setValue; 
} 

Это может иметь более низкую производительность, но он предлагает немного больше безопасности типа.

2

Может быть, вы могли бы заменить:

private Set<T> aSet; 

с

private final Map<T,T> aSet; 

Set, скорее всего, реализуется в виде Map в любом случае.

+0

Вау, мне это нравится! – sixtyfootersdude