Nur bestimmter Typ aus der ArrayList ausgeben.

Juicerz

Mitglied
Hallo,

Ich habe eine kleine Frage.
Sagen wir ich habe folgende ArrayList:

Java:
ArrayList<Nameable> things = new ArrayList<Nameable>();
things.add(new Person("Paul");
things.add(new Animal("Katze");
things.add(new Device("PC");

Und wenn ich jetzt nur die Personen aus der things-liste ausgeben will, wie mache ich das?
Sagen wir ich schreibe eine Methode dafür:

Java:
public void printPerson() {...}

Dann ist es fast ein muss, dass ich vorher noch eine Methode schreiben muss, die die ganzen Enum-Typen miteinander vergleicht oder? Zb.

Java:
    private ArrayList<Nameable> getAllPerson() {
        ArrayList<Nameable> person = new ArrayList<Nameable>();
        for (Nameable z : things) {
            if (z.isType(Type.Person)) {
                person.add(z);
            }
        }
        return person;
    }

Würde ich das als Grundlage nehmen, müsste die vorherige Methode eigentlich nur noch so aussehen?

Java:
public void printPerson() {
    System.out.println(things.getAllPerson());
}

Oder wie müsste das aussehen? Würde es auch ohne die Service-Methode klappen?

Danke! 🙂

*Edit: Sorry die isType-Methode bräuchts natürlich auch noch.

Java:
    private boolean isType(Nameable other){
        if (this.nameable == other) {
            return true;
        } else {
            return false;
        }
    }
 
Zuletzt bearbeitet:
Also generell sollte man die Klassen zeigen, um die es hier geht. Was ist nameable so dass da this.nameable verglichen wird in isType?

Und generell gibt es ja die Prüfungen. Damit die Zuweisung geht, muss ja Person, Device und Animal von Nameable erben. Und wenn man prüfen will, ob etwas eine Person ist, ist instanceof wie von @krschon gesagt, das Mittel der Wahl
 
Sagen wir ich habe folgende ArrayList:
Dann bekommst Du einen Fehler (schließende Klammern) 😛

Mal ein ausführbares Beispiel, das nur die Elemente des angegebenen Typs ausgibt. Subtypen werden hier nicht berücksichtigt, dafür müsste man in Zeile 30 das equals durch isAssignableFrom ersetzen:
Java:
import java.util.ArrayList;
import java.util.List;

public class Test {

    interface Nameable {
        String getName();
    }

    abstract class AbstractNameable implements Nameable {
        private final String name;
        protected AbstractNameable(String name) { this.name = name; }
        public String getName() { return name; }
    }

    class Person extends AbstractNameable { public Person(String name) { super(name); } }
    class Animal extends AbstractNameable { public Animal(String name) { super(name); } }
    class Device extends AbstractNameable { public Device(String name) { super(name); } }

    private List<Nameable> things = new ArrayList<>();

    public Test() {
        things.add(new Person("Paul"));
        things.add(new Animal("Katze"));
        things.add(new Device("PC"));
    }

    private void print(Class<? extends Nameable> type) {
        for (Nameable thing : things) {
            if (thing.getClass().equals(type)) {
                System.out.println(thing.getName());
            }
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println("Personen: ");
        test.print(Person.class);
    }
}
 
Das Beispiel von @mihe7 einmal kurz erweitert:
a) Einmal instanceof benutzt (printPersons Methode) um zu zeigen, wie man Personen gezielt zeigen kann. So es nicht darum ging, mit Class zu arbeiten dürfte das das Ziel der Lektion sein. Diese Prüfungen kommen durchaus regelmäßig vor, z.B. bei der equals Methode.

Hier ist dann auch der Unterschied zu merken: Wenn man eine weitere Klasse von Person ableitet (Manager), dann ist die Prüfung über die Klasse natürlich unterschiedlich. Aber ein Manager ist natürlich auch eine Person - printPersons gibt auch den Manager aus.

b) Wenn man so ein Verhalten mit dynamisch angegebener Klasse nutzen will, dann ist instanceof nicht wirklich zu gebrauchen. Daher einfach die Prüfung über die Methode isAssignableFrom - wie auch schon von @mihe7 genannt. Aber die Prüfung muss natürlich auch umgedreht werden.
Das aber nur der Vollständigkeit-Halber ... denn ich vermute, dass es um diese Dinge nicht wirklich geht (und ich finde das auch nicht wirklich sinnvoll so zu schreiben....)

c) Wenn ich solche Lösungen unsinnig finde, dann muss ich natürlich eine bessere Lösung angeben. Und so aus dem Stehgreif würde ich da eine Lösung mit Predicate<Nameable> nutzen, was dann zur Methode print(Predicate<Nameable>) führt. Dann kann man bei print beliebig filtern. Soll es ein instanceof sein? Ein getName().toLowerCase().startsWith(...)? Was auch immer - jetzt ist die Methode universell nutzbar...

Java:
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;

public class Test {

    interface Nameable {
        String getName();
    }

    abstract class AbstractNameable implements Nameable {
        private final String name;
        protected AbstractNameable(String name) { this.name = name; }
        public String getName() { return name; }
    }

    class Person extends AbstractNameable { public Person(String name) { super(name); } }
    class Animal extends AbstractNameable { public Animal(String name) { super(name); } }
    class Device extends AbstractNameable { public Device(String name) { super(name); } }
    class Manager extends Person { public Manager(String name) { super(name); } }

    private List<Nameable> things = new ArrayList<>();

    public Test() {
        things.add(new Person("Paul"));
        things.add(new Animal("Katze"));
        things.add(new Device("PC"));
        things.add(new Manager("Ackermann"));
    }

    private void print(Class<? extends Nameable> type) {
        for (Nameable thing : things) {
            if (thing.getClass().equals(type)) {
                System.out.println(thing.getName());
            }
        }
    }

    private void print2(Class<? extends Nameable> type) {
        for (Nameable thing : things) {
            if (type.isAssignableFrom(thing.getClass())) {
                System.out.println(thing.getName());
            }
        }
    }

    private void printPersons() {
        for (Nameable thing : things) {
            if (thing instanceof Person) {
                System.out.println(thing.getName());
            }
        }
    }

    private void print(Predicate<Nameable> predicate) {
        for (Nameable thing : things) {
            if (predicate.test(thing)) {
                System.out.println(thing.getName());
            }
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println("Personen: ");
        test.print(Person.class);
        System.out.println("====================");
        test.printPersons();
        System.out.println("====================");
        test.print2(Person.class);
        System.out.println("====================");
        test.print(n -> n instanceof Person);
        System.out.println("====================");
        test.print(n -> n.getName().toLowerCase().startsWith("k"));
    }
}
 
Krass... danke! <3

Ich habe noch eine andere "Variante", könnt ihr die auch mal durchchecken. Sollte eigentlich okay so sein. Mir gehts vor allem um die Methode printNovels, die nur die Romane auszudrucken soll.

Wie würde jetzt das Beispiel mit instanceof aussehen? Irgendwie gibt mir das ein Fehler, was fehlt?

Java:
public void printNovels() {
        for (Book z : readingStuff) {
            if (z instanceof Roman) {
                System.out.println(z.getTitle());
            }
        }
}


Java:
public enum Category {
    
    Buch, Magazin, Roman;

}




public class Book {
    
    private String title;
    private Category category;
    private int pages;
    
    public Book(String title, String category, int pages) {
        this.title = title;
        this.category = Category.valueOf(category);
        this.pages = pages;
    }
    
    public String getTitle() {
        return this.title;
    }
    
    public Category getCategory() {
        return this.category;
    }
    
    public String toString() {
        String bookString = this.title + " " + this.category + " " + this.pages;
        return bookString;
    }

}






public class Bookshelf {
    
    private ArrayList<Book> readingStuff;
    
    public Bookshelf() {
        this.readingStuff = new ArrayList<Book>();
    }
    
    public void addBook() {
        Scanner scan = new Scanner(System.in);
        System.out.print("Titel: ");
        String title = scan.nextLine();
        System.out.print("Kategorie: ");
        String category = scan.next();
        System.out.print("Anzahl Seiten: ");
        int pages = scan.nextInt();
        Book b = new Book(title, category, pages);
        readingStuff.add(b);
    }
    
    public void printNovels() {
        for (Book z : readingStuff) {
            if (z.getCategory().equals(Category.Roman)) {
                System.out.println(z.getTitle());
            }
        }
    }

}
 
Du musst unterscheiden zwischen einer Klassenhierarchie (Also Klassen sind in einer Art Baumstruktur voneinander abgeleitet) und einen Werten.

Bei dem ersten Thema, ging es ja darum zu prüfen, von welcher Klasse eine bestimmte Instanz war. Dazu wurde direkt auf die Klasse zugegriffen (getClass()) oder es wurde der instanceof Operator verwendet.

Aber jetzt geht es Dir um einen Wert einer Variable und nicht um den Typ der Instanz in der Variable.
Das mit der Category ist ähnlich wie z.B. ein int, nur das die Menge der Werte unterschiedlich ist (Achtung: Der Vergleich hinkt massiv! Das nur jetzt etwas grob so betrachten. Eine enum ist streng genommen auch eine spezielle class und ist daher mit einem primitiven Typen nicht wirklich vergleichbar!)

Also wenn Du category als int hättest, dann könnte 0: Buch, 1: Magazin, 2: Roman sein.

Und wenn Du alle Romane ausgeben wolltest, dann würdest Du ja schreiben: getCategory() == 2.

Und das wäre hier jetzt 1:1 auch so: getCategory() == Category.ROMAN wäre hier die Bedingung.
(Die Werte eines Enum werden in der Regel komplett in Großbuchstaben geschrieben und zusammengesetzte Wörter werden mit _ getrennt.)
 
Wenn du schon eine Enumeration für die Kategorie hast, dann solltest du diese auch im Konstruktor verwenden. Das vermindert das Risiko einer "Fehlbedienung" deiner Klasse, denn der String kann ja x-beliebige Werte annehmen, die Kategorie nicht.
Java:
    public Book(String title, Category category, int pages) {
        this.title = title;
        this.category = category;
        this.pages = pages;
    }

Die Methode printNovels() könnte man auch noch etwas flexibler auslegen:
Java:
    public void print(Category category) {
        for (Book book : readingStuff) { // "z" nach "book" geändert, weil "z" ein schlechter Name ist.
            if (book.getCategory().equals(category)) {
                System.out.println(book.getTitle());
            }
        }
    }

// Man könnte jetzt noch Convenience Methoden ergänzen, was allerdings nicht generell sinnvoll ist.

    public void printNovels() {
        print(Category.ROMAN);
    }

Zuletzt noch: Da immutable Klassen vorzuziehen sind und sich ein Buch ja nicht mehr ändert (du hast ja bereits keine Setter), könnte man die Instanzvariablen auch final deklarieren. Ich persönlich mach alles final, was sich nicht mehr ändern soll. Das drückt auch meine Intention für andere aus, die vielleicht meinen Code irgendwann einmal lesen.
Java:
    private final String title;
    private final  Category category;
    private final int pages;
 
Zuletzt bearbeitet:
if (book.getCategory().equals(category)) {
Da aber noch der Hinweis: category könnte auch null sein, d.h. entweder null ebenfalls prüfen oder dann doch auf == wechseln.

JLS 8.9.1 erlaubt dies ja auch explizit:
Because there is only one instance of each enum constant, it is permitted to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.
Ich kann zwar verstehen, dass dies bei Instanzen (und darum handelt es sich ja bei Enum Werten) ungeohnt ist, aber das wäre ein Weg.

Oder eben das
Java:
Objects.equals(book.getCategory(), category)
käme noch als Alternative in Frage....
 

Neue Themen


Zurück
Oben