Abstrakte Klasse und protected Konstruktor

Hallo :slight_smile:

Ich programmiere seit paar Tagen an nem kleinen Client für das Spiel League of Legends, der den Modus Ultimate Bravery emulieren soll.
So und ich habe ein paar Klassen definiert, die die jeweiligen Objekte darstellen (z.B: eine Klasse namens „Item“, das die Items beschreibt, „Champion“ für die Champions etc.)
Das habe ich für die Objektorientierung getan. Aber mich hat an allen was gestört: Vieles war in den Klassen gleich, aber voneinander erben ging auch nicht, da ein Item ja was anderes ist als ein Champion etc. Also habe ich eine neue Oberklasse generiert, von der alle anderen Erben. Da ich aber nciht will, das von der neuen Oberklasse Instanzen erzeugt werden, außer den Unterklassen, habe ich diese Abstrakt mit Protected Konstruktoren gemacht. Aber das funktioniert irgendwie nciht. In meiner Hauptklasse (befindet sich in nem anderen Package) kann ich ohne Compilerfehler ne Instanz dieser Klasse erzeugen. Da die Frage warum? Die LeagueObject Klasse schaut konkret so aus:


import com.youtube.mrschesam.ultimatebravery.MainFrame;
import java.net.URL;
import java.util.Objects;
import javax.swing.Icon;
import javax.swing.ImageIcon;

public abstract class LeagueObject {

    private int ID;
    private String name;
    private static int anzahl;
    protected Icon icon;

    protected LeagueObject(String name, String path, int id) {
        this(name, MainFrame.class.getResource(path), id);
    }

    protected LeagueObject(String name, URL icon, int id) {
        this.name = name;
        this.icon = new ImageIcon(icon);
        this.ID = id;
        anzahl++;
    }

    public Icon getIcon() {
        return icon;
    }

    public String getName() {
        return name;
    }

    public static int getAnzahl() {
        return anzahl;
    }

    protected void setIcon(Icon icon) {
        this.icon = icon;
    }

    public int getID() {
        return ID;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 53 * hash + this.ID;
        hash = 53 * hash + Objects.hashCode(this.name);
        hash = 53 * hash + Objects.hashCode(this.icon);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final LeagueObject other = (LeagueObject) obj;
        if (this.ID != other.ID) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        return Objects.equals(this.icon, other.icon);
    }
  }

Wieso kann ich davon Instanzen in nem anderen Package ohne Vererbung erstellen?

Das Icon ist selbstverständlich “private” deklariert, kanns nur in dem Post nichtmehr editieren^^

Das sollte nicht möglich sein. Vielleicht wird auf alte class-Dateien zurückgegriffen? Baue das ganze Projekt neu, und schaue nach, ob die class-Dateien wirklich die aktuelle Zeit dranhaben.

Mit Vererbung aus technischen gründen sollte man zurückhaltend sein.

Besser wäre es, gemeinsam benötigte Funktionalität (die nicht originär das Verhalten der Objekte beschreibt) in eine eigene Klasse auszulagern, und den Fach-Objekten Instanzen dieser “ServiceObjekte” per Construktor-DI zu injizieren und als Object-Variable zu verwenden.

bye
TT

Da steht überall heute um 08:20 dran, denke mal damit ist dann 20 Uhr gemeint also passt das. Das ganze kompiliert auch und ich kann das Programm ohne Probleme benutzen, keine Exception oder sonstwas.
Der Aufruf in der Mainklasse schaut so aus:

        };```
Hab dafür den Test diesen Konstruktor noch reingeschreiben:
```protected LeagueObject() {
        
    }```

WIeso funktioniert das ohne Probleme?

(Ich sehe meinen vorherigen Post irgendwie nicht)

So wie ich das verstanden habe, habe ich doch genau das getan? Die Methoden, die alle Klassen brauchen stehen da drin, wenn eine diese etwas anders braucht überschreibt diese sie. ABer den letzten Teil “und den Fach-Objekten Instanzen dieser “ServiceObjekte” per Construktor-DI zu injizieren und als Object-Variable zu verwenden.” Verstehe ich irgendwie nciht.

Das funktioniert ohne Probleme weil du
new LeagueObject() {};
geschrieben hast. Jetzt klar?

Wenn nicht dann schreib mal eine leere abstrakte Methode in deine Klasse LeagueObject
public abstract void abstrakteMethode();
Spätestens dann solltest du wissen wofür „{}“ in diesem Zusammenhang steht :wink:

Wenn du nicht willst das ansonsten wo anderst deine Klasse verwendet wird als in deinen erbenden Klassen dann kannst du sie auch in ein eigenes package packen zusammen mit deinen erbenden Klassen und den Konstruktoren von LeagueObject den „default“ (kein protected hinschreiben) Modifier verpassen.

Mir ist klar wofür die {} stehen. Aber die Konstruktoren sind doch trz Protected, sollten also nichtmal sichtbar sein, unter keinen Umständen…

Doch, durch die {} erzeugst du eine anonyme Klasse, die von LeageObject erbt. Das funktioniert, weil du keine abstrakten Methoden hast, die überschrieben werden müssen.

Falls du das mit den anonymen Klassen nicht glaubst, führe mal das aus:

System.out.println(new Date().getClass());
System.out.println(new Date(){}.getClass());

Allerdings dürfte in der anonymen Klasse trotzdem kein Default-Konstruktor zur Verfügung stehen, denn dazu müsste auch LeageObject einen Konstruktor ohne Argumente besitzen.

“protected” heißt aber sichtbar für Klassen im selben Package und erbenden Klassen.
Wenn der instanzierende Code deiner Objekte im gleichen Package liegt - was ich vermute - dann geht das.
Um genau zu sein gibt es keinen Modifier der den Zugriff nur auf erbenden Klassen beschränkt.

Ah ok, danke. Ich dachte da würden dann die Implementierungen der abstrakten Methoden reinkommen als Anonyme Klasse, aber das die dann auch automatisch erbt, wusste ich nicht. Ich denke mal, dann gibt es auch keine bessere Möglichkeit zu erreichen, das zu erreichen, was ich will? Also das davon keine Instanzen erzeugt werden, nur von den Unterklassen, die explizit neue Konstruktoren deklarieren, die public sind.

und @TMII : Nein, sie liegen nicht im selben Package :slight_smile:

Kann ich meine Beiträge nicht editieren? Egal.

Ich hab grad ein Problem, an as ich nicht gedacht habe und mir auch nicht erschließt, wieso das aufkommt. Also ich führe mein Programm in Netbeans aus udn es funktioniert alles. Jetzt habe ich mir aber mal ne Jar erstellt und wollte sie starten. Aber als ich nen Doppelklick drauf machte kam nix, egal wie oft ichs versucht habe. Über die Konsole wurde mir dann auch klar warum: Es fliegt auf einmal ne NullPointerException, obwohl es in der IDE problemlos funktioniert. Sie fliegt in dieser Zeile:

setIconImage(new ImageIcon(MainFrame.class.getResource("\\ressources\\icon.png")).getImage());
Er kann mein png offenbar nicht finden, obwohl es in der Jar vorhanden ist, im selben Package. Wieso findet er die auf einmal nicht mehr? in der IDE funktionierts ja auch, die Package-namen stimmen auch.

[…] obwohl es in der Jar vorhanden ist, im selben Package

Da liegt es dann falsch :wink: Das Bild würde aus dem Ordner \ressources direkt auf Rootebene des Jars geladen werden.

[ot]

Das sollte jetzt eigentlich gehen - das ist nur bei ganz neuen Usern eine Einschränkung. Genauso wie die Tatsache, dass der obige Beitrag erst „freigeschaltet“ werden musste.
[/ot]

Zu dem Thema kannst du mal ins Wiki schauen:
Grafikdateien laden und anzeigen (Java) – Byte-Welt Wiki bzw
Grafikdateien laden und anzeigen (Java) – Byte-Welt Wiki

Im konkreten Fall: Sicher, das das Verzeichnis ressources heißt? Es sollte eigentlich resources heißen (aber das sollte ihn dann auch aus der IDE heraus raushauen). Ansonsten sollten Pfadangaben in JARs grundsätzlich NICHT mit (escapten) backslashes gemacht werden, sondern (auch unter Windows!) mit slashes /:
setIconImage(new ImageIcon(MainFrame.class.getResource("/ressources/icon.png")).getImage());

Und durch den / am Anfang bezieht sich das dann auf den Ordner /ressources, der nicht in einem Package, sondern im Wurzelverzeichnis der JAR liegt (wo er auch liegen sollte)

Zum Debuggen/Testen könnte sowas „sinnvoller“ sein:

URL url = MainFrame.class.getResource("/ressources/icon.png"); // resources oder ressources?!?
System.out.println("Laoding from URL: "+url);
setIconImage(new ImageIcon(url).getImage());

Damit sieht man, was er zu laden versucht - das kann helfen (muss aber nicht ;-))

http://puu.sh/mpnTT/4ca8001b7d.png

So sehe ich das in 7Zip.

Und wenn ich die „//“ in „“ Änder, kommt auhc in Netbeans die Nullpointer :confused:

Kann wieder nicht editieren:

bei
System.out.println("Load from URL: " + BraveryDemo.class.getResource("/ressources/icon.png"));
(Hab die main Methode in ne neue Klasse ausgelagert) kommt raus:

Load from URL: null```

Bei:
```System.out.println("Load from URL: " + BraveryDemo.class.getResource("\\ressources\\icon.png"));```
Kommt 
```Load from URL: file:/C:/Users/Andy/Documents/NetBeansProjects/Ultimate%20Bravery/build/classes/com/youtube/mrschesam/ultimatebravery/%5cressources%5cicon.png```
raus, also das richtige. Wieso ist das so? Und ich habe bis jetz immer mit \\ gearbeitet, ist das wirklich so falsch? Könnte ja zur Not immer den File.separator verwenden, wäre aber sehr umständlich...

wahrscheinlich eher com/youtube/mrschesam/ultimatebravery/ressources/icon.png

wobei wenn das nicht französisch gemeint ist eher resources heisst

Ändere erstmal den Namen in “resources”, und lege ihn nicht in den Package-Ordner, sondern in den “root” Ordner der JAR.

Ansonten ist diese Ausgabe hier

Load from URL: file:/C:/Users/Andy/Documents/NetBeansProjects/Ultimate%20Bravery/build/classes/com/youtube/mrschesam/ultimatebravery/%5cressources%5cicon.png

schon verdächtig: Dort kommen die escapten %5c (backslashes!) drin vor.

Probier’ ggf. die Bilder mal einfach mit ImageIO aus einem Stream zu laden:

InputStream stream = getClass().getResourceAsStream("/resources/icon.png");
System.out.println("Stream: "+stream);
Image image = ImageIO.read(stream);
setIconImage(image);

Stream: null
bei:

        System.out.println("Stream: " + stream);
        try {
            Image image = ImageIO.read(stream);
            System.out.println(image);
        } catch (IOException ex) {
            ex.printStackTrace();
        }```
Das die Ausgabe und dann ne Nullpointer in ImageIO. Ich versteh nicht wieso das nicht funktioniert (Wenn ich die packages im Pfad weglassen und direkt vom Pfad der Klasse ausgehe funktioniert es auch nicht...)

Das ganze funktioniert immer nur mit dem Pfad "\\resources\\icon.png", ansonsten immer nullpointer...
Selbst der hier:
```private static char f = File.separatorChar;

String s = "com" + f + "youtube" + f + "mrschesam" + f + "ultimatebravery" + f + "resources" + f + "icon.png";
``` Funktioniert nicht.

Das ganze ist jetzt ein bißchen verworren, mit mehreren Ansätzen (ja, dazu habe ich auch beigetragen :o ), noch ein kurzer Hinweis: Der Pfad müßte wohl
...getResourceAsStream("/com/youtube/mrschesam/ultimatebravery/resources/icon.png"); statt
...getResourceAsStream( "com/youtube/mrschesam/ultimatebravery/resources/icon.png");
sein, aber wenn das jetzt auch nicht funktioniert, wäre es Zeit, mal “systematisch” auszuprobieren, woran es da hakt…