Protected Inner Classes

S

Steve Schooler

This query best viewed with fixed font.

Having difficulty debugging the following:

pkg1; public interface I1 { void method1(); }
------------------------------------------
pkg2: import pgk1.*;
public class C2
{
protected int i2 = 2;
protected class C2Inner1 implements I1
{
public void method1()
{ System.out.println("This is C2Inner1.method1."); }
}
}
------------------------------------------
pkg3: import pgk1.*;
import pgk2.*;
class C3 extends C2
{
I1 createC2Inner1()
{
C3 z = new C3();
return z.new C2Inner1(); // Line 1
// return new C2Inner1(); // Line 2
// return new C3.C2Inner1(); // Line 3
// return new z.C2Inner1(); // Line 4
}
public static void main(String[] args)
{
C3 a = new C3();
a.i2 = 3; // Line 5
C1return z.new C2Inner1();
I1 x = a.createC2Inner1();
x.method1();
}
}
/*

Line 5 runs okay, which indicates that my understanding of protected is ok.
Lines 1 through 4 all cause the same compile error:

C2Inner1() has protected access in pkg2.C2.C2Inner1

When I change C2Inner1 access to public, Lines 1 through 4 each work okay.
Why can I access i2 but not C2Inner1 (i.e. when both protected)? Is there
different syntax to handle this specific situation?

/*
 
P

P.Hill

Steve Schooler wrote:

pkg3: import pgk1.*;
import pgk2.*;
class C3 extends C2
{
I1 createC2Inner1()
{
C3 z = new C3();
return z.new C2Inner1(); // Line 1
// return new C2Inner1(); // Line 2
// return new C3.C2Inner1(); // Line 3
// return new z.C2Inner1(); // Line 4
}
public static void main(String[] args)
{
C3 a = new C3();
a.i2 = 3; // Line 5
C1return z.new C2Inner1();
I1 x = a.createC2Inner1();
x.method1();
}
}
/*

Line 5 runs okay, which indicates that my understanding of protected is ok.
Lines 1 through 4 all cause the same compile error:

C2Inner1() has protected access in pkg2.C2.C2Inner1

And it does have protected access. You can only get at a protected class
from either the same package or a subclass of the protected class.
Your example shows a subclass of the outer, but not a subclass of the inner.

-Paul
 
P

P.Hill

Steve said:
pkg1; [...]
------------------------------------------
pkg2: import pgk1.*;
[...]
pkg3: import pgk1.*;
import pgk2.*;

So is it pkg or pgk? :)

Since you can't get to a protected in another package, but you can
subclass a protected in another package you can solve the problem
by subclassing the outer class AND the inner class.

package pkg3;

/**
* @author Paul Hill
* @since Aug 10, 2004
*/
import pkg1.*;
import pkg2.*;
class C3 extends C2
{
class C3Inner1 extends C2.C2Inner1 {
}
I1 createC2Inner1()
{
C3 z = new C3();
I1 i1 = z.new C3Inner1(); // Line 1
i1 = new C3Inner1(); // Line 2
i1 = new C3.C3Inner1(); // Line 3
i1 = z.new C3Inner1(); // Line 4
return i1;
}
}

What were you attempting with your line 4?
// return new z.C2Inner1(); // Line 4
I think the right syntax is line 1, but I'd prefer
line 2.

I have deleted your main() because it contained too many typos.
What where you attempting in this main()?
What do you want outside of C3?

Also, let's think about this access. What is the problem with protected on
C2Inner? You have to have a C2 to create a C2Inner, so you can't really
misuse the C2Inner Class even if it was public.

-Paul
 
J

John C. Bollinger

Steve said:
Having difficulty debugging the following:

pkg1; public interface I1 { void method1(); }
------------------------------------------
pkg2: import pgk1.*;
public class C2
{
protected int i2 = 2;
protected class C2Inner1 implements I1
{
public void method1()
{ System.out.println("This is C2Inner1.method1."); }
}
}
------------------------------------------
pkg3: import pgk1.*;
import pgk2.*;
class C3 extends C2
{
I1 createC2Inner1()
{
C3 z = new C3();
return z.new C2Inner1(); // Line 1
// return new C2Inner1(); // Line 2
// return new C3.C2Inner1(); // Line 3
// return new z.C2Inner1(); // Line 4
}
public static void main(String[] args)
{
C3 a = new C3();
a.i2 = 3; // Line 5
C1return z.new C2Inner1();
I1 x = a.createC2Inner1();
x.method1();
}
}
/*

Line 5 runs okay, which indicates that my understanding of protected is ok.
Lines 1 through 4 all cause the same compile error:

C2Inner1() has protected access in pkg2.C2.C2Inner1

When I change C2Inner1 access to public, Lines 1 through 4 each work okay.
Why can I access i2 but not C2Inner1 (i.e. when both protected)? Is there
different syntax to handle this specific situation?
*/

Paul Hill gave you a correct answer (two, in fact), but they might be a
little opaque. It took me some rumination to recognize what was
actually going on (even after reading Paul's responses), so I thought
perhaps you would benefit from a fuller explanation:

You have three relevant classes: pkg2.C2, pkg2.C2.C2Inner1, and pkg3.C3.
Class pkg2.C2.C2Inner1 has protected access in pkg2.C2, and pkg3.C3
extends pkg2.C2. Class pkg3.C3 thus inherits all pkg2.C2's protected
and public members, including the field pkg2.C2.i2 and the inner class
pkg2.C2.C2Inner1. HOWEVER, inheriting an inner class does not
necessarily grant access to that class' members. pkg2.C2.C2Inner1 does
not declare any constructors, so a default constructor is created for
it, ==>and that default constructor has the same access specifier as the
class declaration.<== (JLS (2ed) 8.8.7) pkg3.C3 is not in the same
package as pkg2.C2.C2Inner1 and is not a subclass of it, so it cannot
access the protected constructor, which is exactly what the compiler
told you.

Paul offered a solution to your problem in the form of subclassing
pkg2.C2.C2Inner1 in addition to pkg2.C2. That will work fine, but if
you control the code of pkg2.C2 then there is an alternative, perhaps
preferable solution: give pkg2.C2.C2Innner1 a public constructor.
Here's an example that compiles for me:

====
package pkg2;

import pkg1.I1;

public class C2 {
protected int i2;

protected class C2Inner1 implements I1 {
public C2Inner1() {}
public void method1() { System.out.println("WooHoo!"); }
}
}
====
package pkg3;

import pkg1.I1;
import pkg2.C2;

class C3 extends C2 {

I1 createC2Inner1() {
return this.new C2Inner1();
}
}
====


Cheers,

John Bollinger
(e-mail address removed)
 
P

P.Hill

John said:
Paul offered a solution to your problem in the form of subclassing
pkg2.C2.C2Inner1 in addition to pkg2.C2. That will work fine, but if
you control the code of pkg2.C2 then there is an alternative, perhaps
preferable solution: give pkg2.C2.C2Innner1 a public constructor. Here's
an example that compiles for me:

That fits well with my comment which had a typo in it which should have read
"Also, let's think about this access. What is the problem with [public] on
C2Inner? You have to have a C2 to create a C2Inner, so you can't really misuse
the C2Inner Class even if it was public."

John is providing a compromise between public on the inner class and having to
subclass it. His is simplier, thus superior to my solution.

Looking at the code again, having the factory method suggests that what Steve is
looking for is:

(1) Anyone can get a C3
(2) only get an Inner class from the createCInner1 method, but not have the
ability for a programmer to 'hand craft' one via any other means.

I believe what John provide does a good job of this.

*******************
But Steve may not completely understand inner classes looking at his ideas for
creating one. His original createC2Inner1 returns a new C2Inner which
is NOT inner to the instance of C2 which he called to get it, but inner to
a new C3 (called z in the code). John's code takes Steve's original line 2

// return new C2Inner1(); // Line 2

and explicitly mentions which outer instance is used.

return this.new C2Inner1();

I would assume that is a better guess for what might be needed in this case.
If Steve wanted the I1's being manufactured by createC2Inner1 to each have their
own C2 then createc2Inner1 would make more sense as a static method.
But, if he wants the I1's to link to the current C2 he'll want to use what
John provided.

That is the long way to ask: Steve what is your intended relationship between
each I1 and each C3/C2?

-Paul
 
S

Steve Schooler

Thanks for the thorough and insightful replies. I'm self-training myself in
Java and my primer is Thinking in Java 3rd edition, by Bruce Eckel. Chapter 8
(Interfaces and Inner Classes) exercise 12 (p 368) asks (in effect):

Create interface in pkg1. In pkg2, create class, with protected inner class
that implements interface. In pkg3, inherit from your class, and, inside a
method, return an object of the protected inner class, upcasting to the
interface during the return.

Given the focus on protected access, I infer intent that pkg2 outer class be
inherited, not inner class. With regard to your replies:
Since you can't get to a protected in another package, but you can
subclass a protected in another package you can solve the problem by
subclassing the outer class AND the inner class.

Yes, I'll try this, although I suspect that this isn't what the author
intended. This is (I think) one of the two most natural solutions.
...Class pkg3.C3 inherits all pkg2.C2's protected and public members,
including the field pkg2.C2.i2 and the inner class pkg2.C2.C2Inner1.
However, ... pkg2.C2.C2Inner1 does not declare any constructors, so a
default constructor is created for it, ... (with protected access).
pkg3.C3 ... cannot access the protected constructor.
...give pkg2.C2.C2Innner1 a public constructor.

Probably not what the author intended either, but (I think) the 2nd of the
two most natural solutions.
But Steve may not completely understand inner classes looking at his ideas
for creating one. His original createC2Inner1 returns a new C2Inner which
is NOT inner to the instance of C2 which he called to get it, but inner to
a new C3 (called z in the code).

At the time, I was just trying to avoid compile errors. However, your
insight is right on. I was (and still somewhat am) mentally vague on which
instance a newly created object belongs to. This is simply a lack of
personal focus.
Steve what is your intended relationship between each I1 and each C3/C2?

Beyond the scope of the original exercise, but a very illuminating
(real-world) design issue. Since my design insights are very limited now, I
defer this issue until I have much more java design/coding experience.
 
P

P.Hill

Steve said:
Thinking in Java 3rd edition, by Bruce Eckel. Chapter 8
(Interfaces and Inner Classes) exercise 12 (p 368) asks (in effect):

Create interface in pkg1. In pkg2, create class, with protected inner class
that implements interface. In pkg3, inherit from your class, and, inside a
method, return an object of the protected inner class, upcasting to the
interface during the return.

That's a great little exercise!
Given the focus on protected access, I infer intent that pkg2 outer class be
inherited, not inner class. With regard to your replies:


Yes, I'll try this, although I suspect that this isn't what the author
intended. This is (I think) one of the two most natural solutions.

Okay, you're probably right, but I wouldn't dismiss John's solution so
quickly. It helps to illustrate the difference between a protected class and
a public constructor of a protected inner class.

But thinking about it again -- sometimes it takes 3 times to think about it
enough! -- if the intent is to return an I1 which is a C2Inner1 in ALL CASES,
then I'd say the thing to do is provide a method in C2 that does just that and
forget about doing anything in C3. Maybe that is what led me to a solution
which further subclassed the innerclass. If it wasn't unique to C3, there
wasn't any reason to have a factory method unique to C3.

The interesting lesson to think about is if you create the following method in C2.

protected I1 createC2Inner1()
{
I1 i1 = new C2Inner1();
return i1;
}

and make the inner class and the inner classes constructor protected
( in contradiction to John's solution), then if you can work out why
C3.main(...) becomes the following.

public static void main(String[] args)
{
C3 a = new C3();
a.i2 = 3; // Line 5
// C3.C2Inner1 c2i = a.new C2Inner1(); // Line 6 doesn't compile
I1 x = a.createC2Inner1();
x.method1();
}

Thus why you can get at a protected method, but not a protected inner's
protected constructor, then you'll have the inheretance vs. protected rules down
very well.
I think the last time I looked at Thinking in Java it didn't have an inner class
chapter.

FWIW (which ain't much), one solution to the exercise is to anonymously subclass
C2Inner and write in C3:

public I1 createC2Inner1UsingSubclass()
{
I1 i1 = new C2Inner1() {}; // <-- Notice the { }
return i1;
}

That is basically the same as my original solution, but it is done in many fewer
characters.
Probably not what the author intended either, but (I think) the 2nd of the
two most natural solutions.

So where DO you think he was heading?

Me, I'd probably write the createC2Inner1 in C2 then write
class C3 ... {

public I1 createC2Inner1UsingSuper() {
return super.createC2Inner1();
}
}

It is this last method which matches the requirements: "In pkg3, inherit from
your class, and, inside a method, return an object of the protected inner class,
upcasting to the interface during the return". That is except for the fact that
NO UPCASTING is required to make it the interface, the compiler can see that.
I also but a big hairy comment on both classes talking about what I just talked
about above.

Oh, such problems are too much fun!

-Paul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,738
Latest member
JinaMacvit

Latest Threads

Top