36

I found one example in which buttons are added to panels (instances of JPanel) then panels are added to the the containers (instances generated by getContentPane()) and then containers are, by the construction, included into the JFrame (the windows).

I tried two things:

  1. I got rid of the containers. In more details, I added buttons to a panel (instance of JPanel) and then I added the panel to the windows (instance of JFrame). It worked fine.

  2. I got rid of the panels. In more details, I added buttons directly to the container and then I added the container to the window (instance of JFrame).

So, I do not understand two things.

  1. Why do we have two competing mechanism to do the same things?

  2. What is the reason to use containers in combination with the panels (JPanel)? (For example, what for we include buttons in JPanels and then we include JPanels in the Containers). Can we include JPanel in JPanel? Can we include a container in container?

ADDED:

Maybe essence of my question can be put into one line of code:

frame.getContentPane().add(panel);

What for we put getContentPane() in between? I tried just frame.add(panel); and it works fine.

ADDED 2:

I would like to add some code to be more clear about what I mean. In this example I use only JPane:

import java.awt.*;
import javax.swing.*;
public class HelloWorldSwing {
    public static void main(String[] args) {
        JFrame frame = new JFrame("HelloWorldSwing");
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());        
        panel.add(new JButton("W"), BorderLayout.NORTH);
        panel.add(new JButton("E"), BorderLayout.SOUTH);
        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

And in this example I use only Content Pane:

import java.awt.*;
import javax.swing.*;
public class HelloWorldSwing {
    public static void main(String[] args) {
    JFrame frame = new JFrame("HelloWorldSwing");
    Container pane = frame.getContentPane();
    pane.setLayout(new BorderLayout()); 
    pane.add(new JButton("W"), BorderLayout.NORTH);
    pane.add(new JButton("E"), BorderLayout.SOUTH);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
    }
}

Both work fine! I just want to know if between these two ways to do things one is better (safer).

Lucio
  • 4,753
  • 3
  • 48
  • 77
Roman
  • 124,451
  • 167
  • 349
  • 456

6 Answers6

37

It's not two competing mechanisms - a JPanel is a Container (just look at the class hierarchy at the top of the JPanel javadocs). JFrame.getContentPane() just returns a Container to place the Components that you want to display in the JFrame. Internally, it's using a JPanel (by default - you can change this by calling setContentPane()) As for why it's returning a Container instead of a JPanel - it's because you should program to an interface, not an implementation - at that level, all that you need to care about is that you can add Components to something - and even though Container is a class rather than an interface - it provides the interface needed to do exactly that.

As for why both JFrame.add() and JFrame.getContentPane().add() both do the same thing - JFrame.add() is overridden to call JFrame.getContentPane().add(). This wasn't always the case - pre-JDK 1.5 you always had to specify JFrame.getContentPane().add() explicitly and JFrame.add() threw a RuntimeException if you called it, but due to many complaints, this was changed in JDK 1.5 to do what you'd expect.

Community
  • 1
  • 1
Nate
  • 16,748
  • 5
  • 45
  • 59
  • @Nate, what do you mean by " Internally, it's using a JPanel". You mean that Container is using a JPanel? What do you mean by that? I though that JPanel is a subclass of Container. And what can I change by "setContentPane"? OK. You say that Container is a class (and I always considered Container as a class). But JPane is also a class. Why we cannot use just JPane or just Container? I was able to do the same things by using only JPane. I was also able to do the same using only Containers. – Roman Mar 12 '10 at 14:03
  • `JFrame` is internally using a `JPanel` object to back the `getContentPane()` method. This is why Zachary's answer works. The actual `JPanel` reference is buried in the `JFrame.rootPane` field. You would call `setContentPane()` on the `JFrame` - for example, you could change line 10 in your first example from `frame.add(panel)` to `frame.setContentPane(panel)`. `Container` is a class. `JPanel` is a class (which is a subclass of `Container`). You're confusing the type of the reference with the type of the object the reference references. Think about `Container container = new JPanel()` – Nate Mar 12 '10 at 18:01
  • @Nate the source (http://pragmaticjava.blogspot.com.ar/2008/08/program-to-interface-not-implementation.html) has been removed. – Lucio Apr 26 '14 at 21:20
  • @Lucio Thanks! Changed the link to a question here on StackOverflow about the same thing. – Nate Apr 28 '14 at 13:38
3

Good question. I found it helpful to understand that "Swing provides three generally useful top-level container classes: JFrame, JDialog, and JApplet. ... As a convenience, the add method and its variants, remove and setLayout have been overridden to forward to the contentPane as necessary."—Using Top-Level Containers

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
1

I believe the reason is because Swing was built off of AWT, and Container is a top level AWT object. It really isn't the greatest design choice, though, since you generally don't want to mix AWT (heavyweight) objects with Swing (lightweight).

I think the best way to handle it is to always cast the contentPane to a JPanel.

JPanel contentPanel = (JPanel)aFrame.getContentPane();
Zachary Wright
  • 23,480
  • 10
  • 42
  • 56
1

It is all written in the last version API doc that JFrame.add() (nowerdays) is enough.

You may compare to older Java versions here.

PeterMmm
  • 24,152
  • 13
  • 73
  • 111
1

interesting: jframe.setBackground(color) does not work for me, but jframe.getContentPane().setBackground(color) works.

DanPB
  • 11
  • 1
1

The history and mechanics of this are also discussed in some detail at this leepoint article. Note in particular:

getContentPane() returns a Container object. This isn't really a plain Container object, but is actually a JPanel! This is a Container as a consequence of the hierarchy. So if we get the predefined content pane, it turns out it's actually a JPanel, but we really can't take advantage of the functionality that was added by JComponent.

and

They defined add() methods in JFrame which simply call the corresponding add() methods for the content pane. It seems strange to add this feature now, especially since many layouts use multiple nested panels so you still have to be comfortable with adding directly to a JPanel. And not everything you want to do with the content pane can be done through calls to the JFrame.

Pops
  • 30,199
  • 37
  • 136
  • 151