/ / Необходимост от филтриране на списък към конкретен подклас, използвайки генерични - java, generics, compiler-warnings

Необходимо е да се филтрира списък на конкретен подклас, използвайки генерични - java, generics, compiler-warnings

имам List който съдържа определен суперклас (като Vehicle), и бих искал да напиша метод, който връща обектите в този списък, които са копия на определен подклас (като Car).

Досега имам това, но генерира типично предупреждение за компилатора на операцията:

public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
List<T> result = new ArrayList<T>();

for (Vehicle vehicle : getVehicles()) {
if (type.isAssignableFrom(vehicle.getClass())) {
result.add(type.cast(vehicle)); // Compiler warning here
// Note, (T)vehicle generates an "Unchecked cast" warning (IDE can see this one)
}
}

return result;
}

Warning: Note: Test.java uses unchecked or unsafe operations.

Аз съм добре с всеки друг метод за осъществяване на това (не можех да намеря нищо в него) Collections, но е възможно някой JDK метод да го направи), но в идеалния случай той ще осигури следния интерфейс:

List<Car> cars = getVehiclesOfType(Car.class);

Бих искал да знам защо получавам предупреждение за компилатор на оригиналния код.

Отговори:

5 за отговор № 1

Получавате предупреждение, защото няма начин компилаторът (или IDE) да знае, че актьорът е безопасен, без да се разбира смисъла на isAssignableFrom(), Но isAssignableFrom() не е езикова функция, това е само библиотечен метод. Що се отнася до компилатора, той е същият, както ако сте казали

    if (type.getName().contains("Elvis")) {
result.add(type.cast(vehicle));
}

Но знаете какво isAssignableFrom() Това означава точно такава ситуация @SuppressWarnings е предназначен за.


3 за отговор № 2

Какво ще кажете за това?

if (type.isInstance(vehicle)) {
result.add((T)(vehicle));
}

Дали компилаторът все още се оплаква така?


Но ако бях на ваше място, щях да използвам гуава, което ще направи вашия метод еднолинейно:

public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
return Lists.newArrayList(Iterables.filter(getVehicles(), type));
}

1 за отговор № 3

Проблемът е, че компилаторът не е "умен"достатъчно, за да се знае, че превозното средство е от клас "тип". Това е време за изпълнение и компилаторът не прави "такъв анализ. Има много ситуации като това. Например използвам, ако (вярно) връщане, за да излезете от функцията рано по време на отстраняване на грешки през цялото време. използвайте само връщане, компилаторът осъзнава, че има недостижим код, но с условния, компилаторът не осъзнава, че е невъзможно да влезе в този клон.

Помислете дали да замените условното с if (false) {. Кодът няма никакъв шанс да хвърли изключение, но все още съдържа опасни актове.

По принцип компилаторът казва: "Не мога да потвърдя, че това е безопасно, така че зависи от вас да се уверите, че знаете какво правите." Вашият код не е счупен, просто трябва да внимавате.


1 за отговор № 4

2 неща:

Бих искал да знам защо съм получават предупреждение за компилатора на оригинален код обаче.

1-ви: Вижте кода на класа: той скрива гласове за вас. Обикновено прехвърлянето на произволен тип (T) трябва да бъде предупреждение, но Class.cast всъщност проверява и игнорира предупреждението за компилатора (глупост).

public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException();
return (T) obj;
}

2-ри Това се казва: Generics предупреждения е първото нещо, което трябва да забраните. Като смесица от стар код и сега просто не си струва да потискам предупреждението, нито пък аз се грижа. Аз просто чакам да видя как генериците намаляват ClassCastException и вероятно е вярно само в един случай: използване add вместо addAll (Постави / putAll)


0 за отговор № 5

Може да сте останали с добавяне @SuppressWarning("unchecked") към метода


0 за отговор № 6

Вие оперирате с Vehicle, което е суперклас и се опитвате да сведете до подклас на превозното средство. Това винаги е опасно подаване и ще ви донесе предупреждение за компилатора.

Така че, докато можете да хвърляте от Car да се Vehicle без предупреждение (от Car extends Vehicle), компилаторът няма начин да знае, че променливата е написана като Vehicle всъщност е кола.

Чрез използване на гласове (или чрез използване (Car) или cast(..)), казвате на компилатора, че знаете по-добре. Компилаторът мрази хората и все още издава предупреждение към вас :)


0 за отговор № 7

Лично аз мисля, че поемате грешен подход само функцията време за компилация, Вие зная какъв вид списък ви е необходим по време на компилиране, така че просто създайте помощен метод, който връща правилния вид списък:

public static List<Car> getCars( List<Vehicle> vlist ){ /* code here */ }

Добавете помощния метод към въпросния клас и след това в кода си го направете:

List<Car> cars = Cars.getCars( getVehicles() );

Няма никакви кастинг въпроси. Малко повече код за писане, но можете също да създадете претоварени версии, които връщат само сини коли, само червени коли и т.н.


0 за отговор № 8

Множество отговори на този въпрос твърдят, че предупреждението се дължи на факта, че компилаторът може да "t статично удостоверяване че актьорите не могат да се провалят. Въпреки че компилаторът наистина не може да провери това, това не е причина да предупреждава! Ако компилаторът ще предупреди предупрежденията за всички актьори, които не могат да бъдат проверени статично, това премахва всички значими отливки, защото когато даден акцент може да бъде проверен статично, обикновено не се налага да правите гласове на първо място. С други думи, цялата точка на операцията за гласове е, че мога се провалят по време на изпълнение, което е напълно фина ситуация, която се обработва с a ClassCastException.

Предупреждението за "непроверено подаване", от друга страна, се случва, когато правите гласове, за които има недостатъчна информация за типа, за да се провери подаването по време на изпълнение, Това може да се случи поради изтриването на аргументи тип по време на изпълнение. Да предположим, че сте хвърлили нещо от статичен тип List<?> да се List<Vehicle>, По време на изпълнение, обектите и на двата вида просто имат класа ArrayList или LinkedList като единствена информация за типа на изпълнение, без аргументи тип. Така че компилаторът може да вмъкне някакъв код, който да провери по време на изпълнение, че обектът наистина е a List на Vehicle, Така че компилаторът не прави нищо, но повдига предупреждение за "непроверено подаване".

Това е полезно предупреждение, защото може да се сблъскате с проблем, когато започнете да използвате резултата от актьорския състав. Тъй като резултатът има статичен тип List<Vehicle>, можете да напишете код, който третира елементите от списъка като Vehicle, без да се налага да пише гласове. Но всъщност все още има гласове по време на изпълнение, и то ще се провали, когато List Оказва се, че съдържа всичко, което не е Vehicle, Така че можете да получите ClassCastException в момент, в който не го очаквате.

Безопасният начин за справяне с такава реализация List<?> да се List<Vehicle>, е да претърсва всеки от елементите, да ги прехвърля Vehicle (нещо, което вие мога проверете по време на изпълнение и кой ще повиши a ClassCastException в добре дефинирана точка) и ги добавете към нов списък. Написах генеричен код, за да направя точно това този отговор.