Не понимаю почему не передаються аргументы в List
Должен написать класс House, который имеет поле residents типа List, и метод enter(Object resident). Также есть 4 класса: Dog, Puppy (extends Dog), Cat, Kitty (extends Cat). И суть в том, что метод enter() должен добавлять в класс House животных, но так чтобы если первый элемент при добавлении была кошка, могли добавляться только кошки, соответствующая ситуация с собаками.
Вот так выглядит метод Main.
public class Main {
public static void main(String[] args) {
Dog rex = new Dog("Rax");
Puppy randy = new Puppy("Randy");
Cat barbos = new Cat("Barbos");
Kitten murzik = new Kitten("Murzik");
House dogHouse = new House();
dogHouse.enter(rex);
dogHouse.enter(randy);
dogHouse.enter(murzik); //This must fail on compilation stage if you parameterize the dogHouse. Delete the line when solution is ready
System.out.println(dogHouse);
House catHouse = new House();
catHouse.enter(barbos);
catHouse.enter(murzik);
catHouse.enter(rex); //This must fail on compilation stage if you parameterize the catHouse. Delete the line when solution is ready
System.out.println(catHouse);
}
}
Только начинаю учить дженерики и пробовал такой способ но выдает ошибку, незнаю что делать.
public class House <T> {
private final List <? extends T> residents = new ArrayList<>();
public <T> void enter(T resident) {
residents.add(resident);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("There are following residents in the house:\n");
for (Object resident : residents) {
builder.append(resident.toString()).append("\n");
}
return builder.toString();
}
}
Ответы (3 шт):
Во-первых, в методе main класса Main вы, создавая объект просто от House, используете так называемую "сырую" форму. При таком подходе на место обобщённого типа подставляется класс Object, и поэтому вызывать метод enter объекта класса House вы сможете, передавая в него объект любого типа.
Поэтому при создании объекта нужно сразу явно указать, какой класс вы планируете использовать. Класс собак Dog или класс котов Cat.
То есть, например:
House<Dog> dogHouse = new House<>();
dogHouse.enter(rex);
Во-вторых, в классе House список следует объявить так:
private final List <T> residents = new ArrayList<>();
В-третьих, в методе enter должен быть использован тот обобщённый тип T, который вы указываете классу House, а не собственный явно прописанный тип T, то есть:
public void enter(T resident) {
residents.add(resident);
}
Если внести эти изменения, то действительно на этапе компиляции будут показываться ошибки в тех местах, где вы их ожидаете.
Нужно указать, что класс House является дженериком для некоего типа, унаследованного от класса общего для Cat и Dog (но не Object, так как толку тогда будет мало).
Например, пусть такой класс называется Pet:
@Data
@RequiredArgsConstructor
class Pet {
private final String name;
}
И для него реализована соответствующая иерархия:
Pet
^----- Dog <---- Puppy
|
^----- Cat <---- Kitten
Тогда класс House будет типизирован по какому-то конкретному типу-наследнику Pet:
@Data
class House<T extends Pet> {
private List<T> residents = new ArrayList<>();
public void enter(T pet) {
residents.add(pet);
}
}
Соответственно, его экземпляры должны быть созданы типизированными, а не "сырыми":
House<Dog> dogHouse = new House<>();
// ...
House<Cat> catHouse = new House<>();
и будет получена "нужная" ошибка на этапе компиляции.
При необходимости поселить кошек вместе с собаками, можно будет создать дом, принимающий любых питомцев:
House<Pet> petHouse = new House<>();
petHouse.enter(rex);
petHouse.enter(randy);
petHouse.enter(murzik); // OK!
Если же нужно динамически инициализировать условно "сырую" коллекцию, запоминать тип элемента при первом добавлении и проверять его при всех последующих, это можно реализовать не рекомендуемым следующим образом:
@Data
class RawHouse {
private List<Object> residents = null;
private Class clazz = null;
public void enter(Object pet) {
if (clazz == null) {
clazz = pet.getClass();
residents = new ArrayList<>();
}
if (!clazz.isAssignableFrom(pet.getClass())) {
throw new RuntimeException("Bad pet class: " + pet.getClass());
}
residents.add(pet);
}
}
RawHouse dogHouse = new RawHouse();
dogHouse.enter(rex);
dogHouse.enter(randy);
dogHouse.enter(murzik); // Нет ошибки компиляции, есть ошибка выполнения
System.out.println(dogHouse);
Exception in thread "main" java.lang.RuntimeException: Bad pet class: class Kitten
at RawHouse.enter(SOPetHouses.java:85)
at SOPetHouses.main(SOPetHouses.java:18)
Разумеется, никакая верификация типа и ошибка на этапе компиляции здесь невозможна.
Вам не нужны дженерики чтобы решить задачу. List хранит объекты Object - фактически лишён типа. При вселении проверяется что новый житель имеет тот же тип что и самый первый житель домика. Наследники также пропускаются. Совместимость проверяет выражением residents.get(0).getClass().isInstance(resident). При несовместимости бросаем исключение:
class House {
private final List<Object> residents = new ArrayList<>();
public void enter(Object resident) {
if (
!residents.isEmpty() &&
!residents.get(0).getClass().isInstance(resident)
) {
throw new RuntimeException("AAA!");
}
residents.add(resident);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("There are following residents in the house:\n");
for (Object resident : residents) {
builder.append(resident.toString()).append("\n");
}
return builder.toString();
}
}
