Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4116802

Can a nested class access protected fields inherited by an enclosing class?

XMLWordPrintable

    • generic, unknown, x86, sparc
    • generic, solaris_2.5, solaris_2.5.1, solaris_2.6, windows_95, windows_nt

      Consider a protected field in a class from one package being used in a subclass in another package. The subclass can access that protected field. The compiler, however, refuses to let nested classes of the subclass access them. This seems wrong -- if the subclass and its nested classes can access each other's private fields, it seems bizarre to restrict the nested class from accessing the more-widely-accessible protected fields.

      Take, for example, the following classes:

      package p;

      public class Top {
          protected int f;
      }

      And now its subclass, in the unnamed package:

      import p.Top;

      class Bad extends Top {
          private double d;
          void foo() {
              Object o = new Object() {
                  void bar() {
                      f = 1;
                      d = 1;
                  }
              };
              f = 0;
              d = 0;
          }
      }

      If you compile this, you will find the compiler complaining about the anonymous inner class's assignment to the protected field "f", but not its assignment to the private field "d". Both assignments in foo() itself are allowed.

      None of this happens if Top is in the same package as Bad, of course.

      ====

      The behavior described above is consistent with the inner classes specification
      as currently interpreted by its author. (See comments section.) This issue
      is apparently controversial, so I am re-opening this as a specification bug.

      Old synopsis was: Nested classes cannot access protected fields in outer class

      william.maddox@Eng 1998-03-05


      Name: vi73552 Date: 07/01/99


      The inner class of a child class is not allowed to access
      the protected member of the base class.

      1. Create a child class, i.e PhoenixTableUI, that inherits
         from BasicTableUI. BasicTableUI contains a protected
         attribute named 'table'.
      2. In the child class 'PhoenixTableUI' create an inner class
         called 'MouseInputHandler' that accesses the protected
         attribute, i.e 'table', in the base class.
      3. Compile the code with JDK 1.2.1, and you will get the
         following compilation error:

         Variable table in class javax.swing.plaf.basic.BasicTableUI
         not accessible from inner class
         com.sempra.phoenix.framework.bean.PhoenixTableUI.MouseInputHandler.

      The complete listing of PhoenixTableUI is listed below.


      package com.sempra.phoenix.framework.bean;

      import javax.swing.plaf.basic.*;

      import javax.swing.table.*;
      import javax.swing.*;
      import javax.swing.event.*;
      import java.util.Enumeration;
      import java.util.Hashtable;
      import java.awt.event.*;
      import java.awt.*;
      import javax.swing.plaf.*;
      import java.util.EventObject;

      import javax.swing.text.*;

      // Michael: BasicTableUI does not handle custom editors, so modify it
      // into PhoenixTableUI
      public class PhoenixTableUI extends BasicTableUI
      {
        //
        // The Table's mouse and mouse motion listeners
        //

        /**
         * This inner class is marked "public" due to a compiler bug.
         * This class should be treated as a "protected" inner class.
         * Instantiate it only within subclasses of BasicTableUI.
         */
        public class MouseInputHandler implements MouseInputListener
        {
          // Workaround for mousePressed bug in AWT 1.1
          private boolean phantomMousePressed = false;
          // Component recieving mouse events during editing. May not be editorComponent.
          private Component dispatchComponent;

      // The Table's mouse listener methods.

          public void mouseClicked (MouseEvent e) {}

          private boolean repostEvent(MouseEvent e)
          {
            if (dispatchComponent == null)
            {
              return false;
            }

            MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, dispatchComponent);
            dispatchComponent.dispatchEvent(e2);
            return true;
          }

          private void setValueIsAdjusting (boolean flag)
          {
            table.getSelectionModel().setValueIsAdjusting(flag);
            table.getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
          }

          public void mousePressed(MouseEvent e)
          {
            if (!SwingUtilities. isLeftMouseButton(e))
            {
              return;
            }

            if (phantomMousePressed == true)
            {
              return;
            }

            phantomMousePressed = true;

            Point p = e.getPoint();
            int row = table.rowAtPoint(p);
            int column = table.columnAtPoint(p);
            // The autoscroller can generate drag events outside the Table's range.
            if ((column == -1) || (row == -1))
            {
              return;
            }

            boolean startedNewEditor = table.editCellAt(row, column, e);
            boolean repostEvent;

            // Michael: when updateUI is called, table is set to null,
            // so check for this condition
            if (table == null)
            {
              repostEvent = startedNewEditor;
            }
            else
            {
              repostEvent = table.isEditing() && startedNewEditor;
            }

            if (repostEvent)
            {
              Component editorComponent = table.getEditorComponent();
              Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
              dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
                 p2.x, p2.y);
              repostEvent(e);
            }

            /* Adjust the selection if the event was not forwarded
             * to the editor above *or* the editor declares that it
             * should change selection even when events are forwarded
             * to it.
             */

            // Michael: need to check for custom editors
            if (!repostEvent || table.getCellEditor().shouldSelectCell(e))
            {
              PhoenixCellEditor cellEditor = (PhoenixCellEditor)
                table.getColumnModel().getColumn(column).getCellEditor();
              if (cellEditor == null)
              {
                table.requestFocus ();
              }
              else
              {
                cellEditor.getTableCellEditorComponent ().requestFocus ();
              }
              setValueIsAdjusting (true);
              updateSelection (row, column, e.isControlDown(), e.isShiftDown());
            }
          }

          public void mouseReleased(MouseEvent e)
          {
            if (!SwingUtilities.isLeftMouseButton(e))
            {
              return;
            }

            phantomMousePressed = false;
            repostEvent(e);
            dispatchComponent = null;
            setValueIsAdjusting(false);
          }

          public void mouseEntered(MouseEvent e)
          {
            dispatchComponent = null;
          }

          public void mouseExited(MouseEvent e)
          {
            dispatchComponent = null;
          }

      // The Table's mouse motion listener methods.

          public void mouseMoved(MouseEvent e)
          {
            dispatchComponent = null;
          }

          public void mouseDragged(MouseEvent e)
          {
            if (!SwingUtilities.isLeftMouseButton(e))
            {
              return;
            }

            if (repostEvent(e))
            {
              return;
            }

            Point p = e.getPoint();
            int row = table.rowAtPoint(p);
            int column = table.columnAtPoint(p);
            // The autoscroller can generate drag events outside the Table's range.
            if ((column == -1) || (row == -1))
            {
              return;
            }
            updateSelection(row, column, false, true);
          }
        }

        //
        // Factory methods for the Listeners
        //

        /**
         * Creates the mouse listener for the JTable.
         */
        protected MouseInputListener createMouseInputListener()
        {
            return new MouseInputHandler();
        }

        //
        // The installation/uninstall procedures and support
        //
        public static ComponentUI createUI (JComponent c)
        {
          return new PhoenixTableUI ();
        }

        private void updateSelectionModel (ListSelectionModel sm, int index,
          boolean toggle, boolean extend)
        {
          if (!extend)
          {
            if (!toggle)
            {
              sm.setSelectionInterval(index, index);
            }
            else
            {
              if (sm.isSelectedIndex(index))
              {
                sm.removeSelectionInterval(index, index);
              }
              else
              {
                sm.addSelectionInterval(index, index);
              }
            }
          }
          else
          {
            sm.setLeadSelectionIndex(index);
          }
        }

        private void updateSelection (int rowIndex, int columnIndex,
          boolean toggle, boolean extend)
        {
          // Autoscrolling support.
          Rectangle cellRect = table.getCellRect(rowIndex, columnIndex, false);
          if (cellRect != null)
          {
            table.scrollRectToVisible(cellRect);
          }

          ListSelectionModel rsm = table.getSelectionModel();
          ListSelectionModel csm = table.getColumnModel().getSelectionModel();

          // Update column selection model
          updateSelectionModel(csm, columnIndex, toggle, extend);

          // Update row selection model
          updateSelectionModel(rsm, rowIndex, toggle, extend);
        }

        public void installUI(JComponent c)
        {
          table = (JTable)c;
          super.table = table;

          rendererPane = new CellRendererPane();
          table.add(rendererPane);

          installDefaults();
          installListeners();
          installKeyboardActions();
        }

        public void uninstallUI(JComponent c)
        {
          uninstallDefaults();
          uninstallListeners();
          uninstallKeyboardActions();

          table.remove(rendererPane);
          rendererPane = null;
          table = null;
          super.table = null;
        }

      }
      (Review ID: 85088)
      ======================================================================

      From 4196228:

      Name: wm38563 Date: 12/09/98

      The bug still exists, but the compiler alows

      DerivedClass.this.protectedMethod()

      in the inner class.

      Then java throws an access exception at runtime.

      The compiler still complaines about calls to
      protectedMethod() in the DerivedClass's InnerClass.
      (Review ID: 47287)
      ======================================================================

            wmaddoxsunw William Maddox (Inactive)
            karnoldsunw Kenneth Arnold (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: