auf superklasse zugreifen

pappender

Aktives Mitglied
Hallo,

ich habe eine Oberklasse (View/Hauptfenster):
Java:
public class View extends JFrame {

    private Controller controller;
   
    private JPanel mainPanel;
   
    private int windowWidth, windowHeight; 
   
   
    public View(Controller controller) {
       
        super("IDS");
        this.controller = controller;
       
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle r = ge.getMaximumWindowBounds();
        windowWidth = r.width;
        windowHeight = r.height;
        setPreferredSize(new Dimension(windowWidth, windowHeight));

        mainPanel = new JPanel();
        add(mainPanel, BorderLayout.CENTER);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setVisible(true);
       
    }


}

Und eine weitere Subklasse in einer anderen Datei:
Java:
public class ViewEinsatzplan {

    private JPanel mainPanel;
   
    public void ViewShow() {
       
       
    }
   
}

Wie kann man auf die Superklasse zugreifen?
Zum Beispiel um im Hauptfenster ein JPanel hinzuzufügen?
 
"ViewEinsatzplan" ist keine Subklasse von "View". Damit sie zur Subklasse wird musst du hinter class ViewEinsatzplan" noch ein "extends View" hinschreiben, wie du es in "View" mit JFrame getan hast. dann kannst du auf die super klasse mit "super" zugreifen, so wie du es in view mit den Methoden aus JFrame aucht un könntest und auch solltest, wenn du ne Methode gleichen Names in deiner Klasse hast und die SUperversion haben willst.
 
Allerdings sollte man nur von Klassen ableiten, wenn man diese auch erweitert. Da du das nicht machst, würde ich Auch nicht von JFrame ableiten, sondern das JFrame einfach als Instanzobjekt der Klasse View definieren.

Gruß

Claus
 
Ah okay danke. So geht's ja.
Aber ich sehe die Änderung nicht. Ich habe beispielsweise ein JPanel zugefügt und den Hintergrund schwarz gemacht, sehen tue ich aber nichts.

Java:
public class ViewEinsatzplan extends View {
   
    public ViewEinsatzplan(Controller controller) {
        super(controller);
    }
   
    private JPanel mainPanel;
   
    public void ViewShow() {
       
        mainPanel = new JPanel();
        super.add(mainPanel, BorderLayout.CENTER);
        mainPanel.setBackground(Color.BLACK);
        super.validate();
        super.repaint();
       
    }
   
}

@Thallius
Was meinst du mit 'nicht erweitern'?

Ich habe ja die HauptView, die das Fenster und die MenüBar aufbaut.
Die Unterklassen für die Menüpunkte bauen den Rest des Fensters auf.
 
DU erweiterst JFrame ja nicht, von daher sollte man sich lieber ne private Instanzvariable vom Typ JFrame machen und auf dem Objekt alle Methoden ausführen. So sollte man es immer machen, wenn man nicht explizit Methoden von JFrame überschreiben möchte oder es erweitern. Allerdings wird in vielen Tutorials und GUIBuildern von JFrame abgeleitet, aber guter Stil ist es trz nicht.
 
Achso, hab ich verstanden. Das werde ich dann mal abändern.
Und dann kann ich sicherlich auch in der Subklasse auf JFrame zugreifen und weitere Komponenten hinzufügen?
 
Hallo,

irgendwie will das mit meinen gettern und settern nicht funktionieren, was mache ich falsch?
Java:
public class View {

    private Controller controller;
 
    private JFrame mainFrame;
 
    public View(Controller controller) {     
        this.controller = controller;     
        mainFrame = new JFrame();
     
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle r = ge.getMaximumWindowBounds();
        windowWidth = r.width;
        windowHeight = r.height;
        mainFrame.setPreferredSize(new Dimension(windowWidth, windowHeight));

        menubar();
        mainPanel = new JPanel();
        mainFrame.add(mainPanel, BorderLayout.CENTER);
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.pack();
        mainFrame.setVisible(true);     
    }
    //==================================================
    // setter & getter
    //==================================================
    public void setMainFrame(JFrame name) {
        mainFrame = name;
    }
 
    public JFrame getMainFrame() {
        return mainFrame;
    }

Und nun möchte ich auf JFrame aus einer anderen Klasse, die in einer eigenen Datei steht zugreifen:
Java:
public class ViewEinsatzplan extends View {
 
    public ViewEinsatzplan(Controller controller) {
        super(controller);
    }
 
    JFrame mainFrame = getMainFrame();
 
    private JPanel mainPanel;
    private JButton b1;
 
    public void ViewShow() {
     
        mainPanel = new JPanel();
        mainPanel.setBackground(Color.BLACK);
     
        b1 = new JButton("Test");
        mainPanel.add(b1);
     
        mainFrame.add(mainPanel, BorderLayout.CENTER);
        mainFrame.validate();
        mainFrame.repaint();     
    } 
}

Aber irgendwie passiert nichts. Ein Fehler wird aber auch nicht ausgeworfen.
 
Zuletzt bearbeitet von einem Moderator:
Das Problem ist das Attribut mainFrame in deiner Klasse ViewEinsatzplan.
Diese Klasse erbt von View, die Klasse View besitzt schon ein Attribut vom Type JFrame mit den Namen mainFrame. Sofern dieses Attribut nicht private ist, hast du in jeder Subklasse Zugriff auf diese Attribut der Oberklasse (Grundlagen Vererbung).
Lösung: Lösch einfach das Attribut mainFrame in der Subklasse.
 
Hm, ich denke man soll diese Variablen privat gestalten und mittels Setter und getter darauf zugreifen ?

Ich hab JFrame mainFrame auf protected geändert und das Attribut in der Subklasse entfernt. Aber es ändert sich trotzdem nichts.
 
Hm, ich denke man soll diese Variablen privat gestalten und mittels Setter und getter darauf zugreifen ?
Du kannst das Attribut in der Superklasse auch private lassen, und mittels getter abfragen. Üblicher ist es aber die Sichtbarkeit auf protected zu ändern und direkt auf das Attribut zuzugreifen.

Aber es ändert sich trotzdem nichts.
Leider kann ich nicht erraten wie dein Code denn aktuell auschaut und ob es richtig geändert wurde 😉
 
Das kann ich ändern:

Hier die Hauptklasse, zu Testzwecken habe ich den Hintergrund des JPanels Blau gestaltet und einen Button "Test1" plaziert:

Java:
package xxxx;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class View {

    private Controller controller;
  
    protected JFrame mainFrame;
  
    private JMenuBar menubar;
    private JMenu menuEplan;
    private JMenuItem menuEplanOperationplan;
  
    protected JPanel mainPanel;
  
    private JButton b1;
  
    private int windowWidth, windowHeight;
  
  
  
  
  
    public View(Controller controller) {
      
        this.controller = controller;
      
        mainFrame = new JFrame();
      
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle r = ge.getMaximumWindowBounds();
        windowWidth = r.width;
        windowHeight = r.height;
        mainFrame.setPreferredSize(new Dimension(windowWidth, windowHeight));
        mainFrame.setTitle("IDS");

        menubar();
        mainPanel = new JPanel();
        mainPanel.setBackground(Color.BLUE);
      
        b1 = new JButton("Test1");
        mainPanel.add(b1);
      
        mainFrame.add(mainPanel, BorderLayout.CENTER);
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.pack();
        mainFrame.setVisible(true);
      
    }
  
  
  
  
    //==================================================
    // setter & getter
    //==================================================
    //public void setMainFrame(JFrame mainFrame) {
    //    this.mainFrame = mainFrame;
    //}
  
    //public JFrame getMainFrame() {
    //    return this.mainFrame;
    //}
  
  
  
  
  
    //==================================================
    // Menubar
    //==================================================
    public void menubar() {
      
        menubar                    = new JMenuBar();
      
        menuEplan                = new JMenu("Einsatzplan");
        menuEplanOperationplan    = new JMenuItem("Einsatzplan");
        menuEplanOperationplan.addActionListener(new MenuActionListener());
      
        menuEplan.add(menuEplanOperationplan);
        menubar.add(menuEplan);
      
        mainFrame.add(menubar, BorderLayout.PAGE_START);
      
    }
  
    public class MenuActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            controller.handleMenu(arg0);
        }
    }
  
  
  
  
  
    //==================================================
    // JPanel
    //==================================================
    public void updateMenu(Model model) {
        mainPanel.removeAll();
        mainPanel.validate();
        mainPanel.repaint();
        System.out.println("Test");
        try {
            Class.forName(model.getClassString());
        } catch (ClassNotFoundException e) {
            JOptionPane.showMessageDialog(null, e, "IDS - Fehler", JOptionPane.ERROR_MESSAGE);
        }
    }
  
  
  
  
  
}


Und die geänderte Subklasse, hier soll zu Testwecken die Hintergrundfarbe des JPanels auf schwarz gesetzt werden und ein Button "Test2" plaziert werden:

Java:
package xxx;

import java.awt.Color;

import javax.swing.JButton;

public class ViewEinsatzplan extends View {
  
    public ViewEinsatzplan(Controller controller) {
        super(controller);
    }
  
    private JButton b2;
  
    public void ViewShow() {
      
        mainPanel.setBackground(Color.BLACK);
      
        b2 = new JButton("Test2");
        mainPanel.add(b2);
      
        mainPanel.validate();
        mainPanel.repaint();
      
    }
  
}

Vielleicht liegt es ja auch daran, dass das JPanel neugezeichnet werden muss?
Es funktioniert nur nicht richtig, bzw. garnicht.
 
Gut wie rufst du deinen Code auf? Wo wird die Instanz von "ViewEinsatzplan" erstellt und wo die Methode "ViewShow" aufgerufen? (Methoden werden in Java in lowerCamelCase geschrieben)
 
Okay lowerCamelCase passe ich dann noch an.

Also hier der Rest:

Mainklasse:
Java:
package xxx;

public class Main {
  
    public static void main(String[] args) {
        new Controller();
    }
}

Controller:
Java:
package xxx;

import java.awt.event.ActionEvent;

public class Controller {
  
    private Model model;
    private View view;
    private ViewEinsatzplan ViewShow;
  
    public Controller(){
        model = new Model();
        view = new View(this);
    }

    public void handleMenu(ActionEvent arg0) {
        model.classString(arg0);
        view.updateMenu(model);
    }
  
}

Model:
Java:
package xxx;

import java.awt.event.ActionEvent;

public class Model {
  
    private String classString;
  
    public void classString(ActionEvent arg0) {
        classString = "xxx.View"+arg0.getActionCommand();
    }
  
    public String getClassString() {
        return classString;
    }

}
 
Du sagst die Klasse ViewEinsatzplan soll die Farbe des Panels ändern.
Solange du aber kein Objekt von ViewEinsatzplan erzeugst und die entsprechende Methode aufrufst wird nichts passieren.

Laut deinem Code erzeugt der Controller nur ein View Objekt. Dabei handelt es sich aber nur um ein View nicht um ein ViewEinsatzplan!
 
Achso. Dann ist mein Gedankenansatz falsch?
Ich habe ja die View (HauptView), diese baut das Fenster auf, man könnte wohl auch StartView sagen.

Für jeden Menüpunkt der MenuBar wollte ich eine eigene View erstellen (in dem Fall die ViewEinsatzplan), die dann dementsprechend das Fenster weiteraufbaut.

Und ich muss in der ViewEinsatzplan noch extra eine Methode einfügen, die den Rest des Fensters aufbaut?
 
So wollte ich die Klasse ViewEinsatzplan aufrufen, geht aber nicht.
Java:
    public void updateMenu(Model model) {
        try {
            Class<?> newClass = (Class<?>) Class.forName(model.getClassString()).newInstance();
            newClass.viewShow();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            JOptionPane.showMessageDialog(null, e, "IDS - Fehler", JOptionPane.ERROR_MESSAGE);
        }
        System.out.println("Test");
    }
 
Gut abgesehen davon: Sobald du hier ein neues Objekt deiner Klasse ViewEinsatzplan erstellst wird ein komplett neuer JFrame erstellt.
Und nicht wie von dir erwartet das vorhande JFrame Objekt bearbeitet.
 
Hm, jetzt versteh ich gar nichts mehr.
Wo erstelle ich denn ein neues JFrame?

Kann doch nicht so kompliziert sein, eine Subklasse das Fenster weiter aufbauen zu lassen.
 
Wo erstelle ich denn ein neues JFrame?

Dein Klasse View erstellt im Konstruktor ein JFrame Objekt.
Deine Klasse ViewEinsatzplan leitet von View ab (sprich erweitert die View Klasse um Funktionalität)

Jedes Objekt von ViewEinsatzplan ist auch ein ViewObjekt! Aber nicht jedes ViewObjekt ist ein ViewEinsatzplan Objekt 😉
Das ist eine der Grundlagen von Vererbung.

Um es etwas genauer zu erklären:
Java:
public class View {
  protected JFrame mainFrame;
   
  public View(Controller controller) {   
  mainFrame = new JFrame();   
  }
}

Java:
public class ViewEinsatzplan extends View {
  public ViewEinsatzplan(Controller controller) {
  super(controller);
  }
}
Der Konstruktor der Klasse ViewEinsatzplan ruft mit super(...) den entsprechenden Konstruktor der Superklasse auf.
Sprich sobald du ein neues View Objekt erzeugst wird ein JFrame erzeugt, und sobald du ein ViewEinsatzplan Objekt erstellst wird ebenfalls ein JFrame erzeugt.


Kann doch nicht so kompliziert sein, eine Subklasse das Fenster weiter aufbauen zu lassen.
Ist es auch nicht, aber gewisse Grundlagen von Vererbung und OOP sollten sitzen. Das ist eine der Grundvorraussetzung der UI Programmierung.

Vereinfacht: ViewEinsatzplan muss nicht von View ableiten. ViewEinsatzplan erstellt ein JPanel mit allen gewünschten Komponenten und man kann per getter auf diese JPanel zugreifen.
Nun brauchst du einfach nur ein Objekt von ViewEinsatzplan erzeugen und dir per getter das JPanel holen und deiner View hinzufügen.

Java:
public class View {
  private Controller controller;
  protected JFrame mainFrame;
  public View(Controller controller) {   
  this.controller = controller;   
  mainFrame = new JFrame();   
  }
   
  public void menuEinsatzplanClicked() {
     ViewEinsatzplan subView = new ViewEinsatzplan();
     subView.initialize();
     mainFrame.add(subView.getPanel());
  }
}

Java:
public class ViewEinsatzplan {
   private JPanel panel;
   public ViewEinsatzplan() {
   }

   public void initialize() {
     panel = new JPanel();
     // alle weiteren anweisungen um das Panel mit den gewünschten komponenten zu füllen
   }
   
   public JPanel getPanel() {
   }
}
 
Okay, erstmal großen Dank an @Joose,

du hast mir ein kleines Licht aufgehen lassen.
Allerdings muss ich noch viel lernen was OOP angeht.
Aber Übung macht den Meister.

Ich habe also meine StandardView die ein JFrame erstellt und eine Menüleiste erzeugt. Mit einem Klick auf einen der Menüpunkte wird dynamisch die entsprechende Klasse geladen, die die View entsprechend weiter aufbauen soll:
Java:
public class View {

    private Controller controller;
 
    protected JFrame mainFrame;
 
    private JMenuBar menubar;
    private JMenu menuEplan;
    private JMenuItem menuEplanOperationplan;
 
    private JPanel mainPanel;
 
    private int windowWidth, windowHeight;
  
    public View(Controller controller) {
     
        this.controller = controller;
     
        mainFrame = new JFrame();
     
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle r = ge.getMaximumWindowBounds();
        windowWidth = r.width;
        windowHeight = r.height;
        mainFrame.setPreferredSize(new Dimension(windowWidth, windowHeight));

        menubar();
        mainPanel = new JPanel();
        mainPanel.setBackground(Color.GREEN);
        mainFrame.add(mainPanel, BorderLayout.CENTER);
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.pack();
        mainFrame.setVisible(true);     
    }
    //==================================================
    // Menubar
    //==================================================
    public void menubar() {
     
        menubar                    = new JMenuBar();
     
        menuEplan                = new JMenu("Einsatzplan");
        menuEplanOperationplan    = new JMenuItem("Einsatzplan");
        menuEplanOperationplan.addActionListener(new MenuActionListener());
     
        menuEplan.add(menuEplanOperationplan);
        menubar.add(menuEplan);
     
        mainFrame.add(menubar, BorderLayout.PAGE_START);     
    }
 
    public class MenuActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            controller.handleMenu(arg0);
        }
    }
    //==================================================
    // Menü - entsprechende Klasse laden
    //==================================================
    public void updateMenu(Model model) {     
        try {         
            mainFrame.remove(mainPanel);
         
            Class<?> subView = Class.forName(model.getClassName());
            Object o = subView.newInstance();
            Method getMethodIni = subView.getMethod("initialize");
            getMethodIni.invoke(o);
         
            Method getMethodPanel = subView.getMethod("getPanel");
            mainFrame.add((Component) getMethodPanel.invoke(o));
         
            mainFrame.validate();
         
        } catch (ClassNotFoundException | ClassCastException | IllegalAccessException | IllegalArgumentException | SecurityException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            JOptionPane.showMessageDialog(null, e, "IDS - Fehler", JOptionPane.ERROR_MESSAGE);         
        }     
    }
}

Und die View, die das Fenster mittels JPanel weiter aufbaut:
Java:
public class ViewEinsatzplan {  
    private JPanel mainPanel;

    public void initialize() {
        mainPanel = new JPanel();
        mainPanel.setBackground(Color.BLACK);
    }
  
    public JPanel getPanel() {
        return mainPanel;
    }
}


Kann man das jetzt so gelten lassen, oder gibt es noch Kritik/weitere Anmerkungen?
 
Zuletzt bearbeitet von einem Moderator:

Zurück
Oben