In effective java second edition, the author says,
It is possible, although rare, to establish the association manually
using the expression enclosingInstance.new MemberClass(args). As you
would expect, the association takes up space in the nonstatic member class
instance and adds time to its construction.
What does enclosingInstance.new MemberClass(args) mean? I googled for this and couldn't find anything. Can someone explain to me and shed some light? I have to take a seminar on this topic...
It means "create a new instance of MemberClass, using enclosingInstance as the reference for the new instance".
An inner class has an implicit reference to its enclosing class - normally if you just call new MemberClass() within an instance method, the enclosing class instance is implicitly this, but using enclosingInstance.new MemberClass() you can explicitly specify a different one. You can also use this approach to create an instance of the inner class from a static method, or indeed from an entirely different class.
A demonstration may help to explain:
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
class Inner {
void showEnclosingName() {
System.out.println("Enclosing instance name: " + name);
}
}
void demo() {
Outer outer = new Outer("Other Outer");
Inner inner1 = new Inner(); // Implicitly this.new Inner();
Inner inner2 = outer.new Inner();
inner1.showEnclosingName(); // Prints Original Outer
inner2.showEnclosingName(); // Prints Other Outer
}
public static void main(String[] args) {
Outer outer = new Outer("Original Outer");
outer.demo();
}
}
Related
public class Outer{
public class Inner{
}
}
public class Main {
public static void main(String[] args) {
Outer objOut = new Outer();
//Outer.Inner object1= objOut.new Inner(); // runes without a problem
//objOut.Inner object2= objOut.new Inner(); //gives error
}
}
This might sound little amateur but, What are the difference between Outer.Inner vs objOut.Inner.
You cannot use a variable name as the type of another variable, which is what you're trying to do with objOut.Inner. The type of the variable is Inner (or optionally Outer.Inner).
Because Inner is an inner class, it's associated with an instance of its outer class (its enclosing instance). When you create an instance of it, you can optionally¹ specify what object instance it's associated with, which is what you're doing with objOut.new Inner.
This example may help make it a bit clearer:
public class Example {
private String str;
public class Inner {
void show() {
// Show the string for the Example this Inner is part of
System.out.println(Example.this.str);
}
}
public Example(String s) {
this.str = s;
}
public static void main(String[] args) {
Example e1 = new Example("e1");
Example e2 = new Example("e2");
Inner i1 = e1.new Inner();
i1.show(); // "e1"
Inner i2 = e2.new Inner();
i2.show(); // "e2"
}
}
Live Copy
Notice how the i1 Inner instance gets e1 as its enclosing Example instance, and so sees e1's str, but i2 gets e2 as its enclosing instance so it sees e2's str.
I suggest this Java tutorial for more information.
¹ Sometimes it's not optional, such as in my Example class above, since where new Inner is used, there's no default instance it could use. It would be optional in an instance method of Example, but not in that static method.
This one :
Outer.Inner = objOut.new Inner();
will not compile but if you change it to :
Outer.Inner object = objOut.new Inner();
will mean creating an instance of inner class which has reference to Outer class - objOut will be instance of Outer class.
Also this :
objOut.Inner = objOut.new Inner();
will not compile since objOut which is the instance of Outer class does not have poperty Inner.
And it is not Outer class that knows the instance of Inner class - it is Inner class instance that knows the Outer class instance with which it was created.
EDIT
The line :
objOut.Inner object2= objOut.new Inner();
will not compile since Inner type identifer belongs to Outer class and not instance of this class.
Both don't compile.
To make the former compile, a variable should be declared, and the name to that variable should be given.
Outer.Inner obj = objOut.new Inner();
The latter wouldn't compile even if you did this step since objOut.Inner is neither a type (because the primary expression objOut is not a type) nor a valid name (because . is not allowed within an identifier (jls-3.8)).
A simplified rule (jls-14.4) for your case would be
LocalVariableType VariableDeclaratorId [= VariableInitializer];
I read that an instance of an inner class cannot be created without an instance of outer class. But when I tried to create an instance of my inner class using it as an instance member of my outer class, it worked.
I understand that it is creating an inner object through a reference to my outer class object, but is it the right way to do it?
Below is my code snippet:
public class TestInner {
private Nonstatic non = null;
private static int access = 4;
public class Nonstatic {
void hello() {
access = 90;
}
}
public static void main(String[] args) {
TestInner outer = new TestInner();
TestInner.Nonstatic innern= outer.new Nonstatic();
System.out.println("Non static obj1 is "+innern);
outer.testinnerObj();
}
public void testinnerObj() {
non = new Nonstatic();
System.out.println("Non static obj2 is "+non);
non.hello();
}
}
You're writing "An instance of Inner class cannot be created without an instance of outer class". And that's exactly what you are doing.
First, you create an instance of the "outer" class:
TestInner outer = new TestInner();
Then, you create an instance of the "inner" class - it only lives
in the scope of outer:
TestInner.Nonstatic innern= outer.new Nonstatic();
So, the question maybe boils down to this: yes, you are creating the object in the static main method. But that does not matter, because you are using the syntax outer.newwhich creates it in the scope of outer.
Hope that helps.
Is there any way to access the methods of local inner classes in Java. Following code is the sample code that I tried before. According to that what is the mechanism to access the mInner() method?
class Outer{
int a=100;
Object mOuter(){
class Inner{
void mInner(){
int y=200;
System.out.println("mInner..");
System.out.println("y : "+y);
}
}
Inner iob=new Inner();
return iob;
}
}
class Demo{
public static void main(String args[]){
Outer t=new Outer();
Object ob=t.mOuter();
ob.mInner(); // ?need a solution..
}
}
As ILikeTau's comment says, you can't access a class that you define in a method. You could define it outside the method, but another possibility is to define an interface (or abstract class). Then the code would still be inside your method, and could access final variables and parameters defined in the method (which you couldn't do if you moved the whole class outside). Something like:
class Outer {
int a = 100;
public interface AnInterface {
void mInner(); // automatically "public"
}
AnInterface mOuter() { // note that the return type is no longer Object
class Inner implements AnInterface {
#Override
public void mInner() { // must be public
int y = 200;
System.out.println("mInner..");
System.out.println("y : " + y);
}
}
Inner iob = new Inner();
return iob;
}
}
class Demo {
public static void main(String[] args) { // the preferred syntax
Outer t = new Outer();
Outer.AnInterface ob = t.mOuter();
ob.mInner();
}
}
Note: not tested
Note that the return type, and the type of ob, have been changed from Object. That's because in Java, if you declare something to be an Object, you can only access the methods defined for Object. The compiler has to know, at compile time (not at run time) that your object ob has an mInner method, and it can't tell that if the only thing it knows is that it's an Object. By changing it to AnInterface, the compiler now knows that it has an mInner() method.
The scoping rules of a local class are pretty much the same as the scoping rules of a variable, that is, it is confined to the enclosing block.
The same way you cannot access variable iob from main, you cannot access local class Inner from main.
Outside the enclosing block, there's no difference between a local class and an anonymous class. Neither can be accessed. The difference is that within the enclosing block, the local class can be accessed by name, especially useful if you need to access it repeatedly, e.g. to create multiple instances.
The only way to interact with a local/anonymous class outside the enclosing block, is through any superclass or interface implemented by the class in question.
To access the inner class create an object of inner class..
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
from your example
outer t=new outer();
outer.inner inner1=t.new inner();
Hope this helps you...
If a class is private then must the constructor be private as well?
No, there is no such restriction. See JLS §8.8.3. Constructor Modifiers.
It's worth pointing out that only a nested class can be declared private. The JLS permits the constructors for such a class to use any valid access modifiers.
If you mean nested class, the answer is no. Making the inner class private makes it only usable within the outer class.
Edit: It appears that outer classes have full access to the innards of the inner classes regardless of their access modifiers. This invalidates my above reasoning, but regardless, there is no such restriction. Curiously though, now it appears that if the inner class is private, its constructor is essentially private, regardless of its access modifier, since noone else can call it.
No it hasn't. On the contrary, if you create an instance of the inner class using a private constructor (which is default for a private class) from the outer class Java will create an additional class to prevent access violation and keep JVM happy
If you compile this class
class Test {
private class Test2 {
Test2() {
}
}
Test() {
new Test2();
}
}
javac will create Test.class, Test#Test2.class
and if you compile this class
class Test {
private class Test2 {
}
Test() {
new Test2();
}
}
javac will create Test.class, Test#Test2.class, Test$1.class
No it is not fix, you can set it private/public/any you want.
But in some case I prefer to make constructor private, when you don't want to allow other classes to create object of this class. then in that case you can do something like this, by setting constructor private.
private class TestClass{
private TestClass testClass=null;
private TestClass(){
//can not accessed from out side
// so out side classes can not create object
// of this class
}
public TestClass getInstance(){
//do some code here to
// or if you want to allow only one instance of this class to be created and used
// then you can do this
if(testClass==null)
testClass = new TestClass();
return testClass;
}
}
Btw it depends on your requirement.
It does not have to be private. But it can. Example:
public class Outer {
// inner class with private constructor
private class Inner {
private Inner() {
super();
}
}
// this works even though the constructor is private.
// We are in the scope of an instance of Outer
Inner i = new Inner();
// let's try from a static method
// we are not in the scope of an instance of Outer
public static void main(String[] args) {
// this will NOT work, "need to have Inner instance"
Inner inner1 = new Inner();
// this WILL work
Inner inner2 = new Outer().new Inner();
}
}
// scope of another class
class Other {
// this will NOT work, "Inner not known"
Inner inner = new Outer().new Inner();
}
It doesn't make a difference if you use private or public constructor on private inner classes. The reason is that the inner class instance is part of the outer class instance. This picture says it all:
Note that we are talking about an inner class. If the nested class was static, the official terminology is static nested class, which is different from an inner class. A public static nested class would be accessible without outer class instance just by calling new Outer.Inner(). See here for more information about inner- and nested classes. http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
In Java, whenever an inner class instance is created, it is associated with an instance of an outer class. Out of curiosity, is it possible to associate the inner class with another instance of an outer class instead?
Yes, this is possible, although it sounds like a really bad idea to me. The idea is to set the otherwise final pointer to the outer instance using reflection (which is not guaranteed to succeed).
import java.lang.reflect.*;
public class Me {
final String name;
Me(String name) {
this.name = name;
}
class InnerMe {
String whoAreYou() {
return name;
}
}
InnerMe innerSelf() {
return new InnerMe();
}
public static void main(String args[]) throws Exception {
final Me me = new Me("Just the old me!");
final InnerMe innerMe = me.innerSelf();
System.out.println(innerMe.whoAreYou()); // "Just the old me!"
Field outerThis = innerMe.getClass().getDeclaredFields()[0];
outerThis.setAccessible(true);
outerThis.set(innerMe, new Me("New and improved me!"));
System.out.println(innerMe.whoAreYou()); // "New and improved me!"
}
}
The crucial part here is outerThis.setAccessible(true); -- a SecurityManager could enforce a policy that prohibits this from succeeding.
If you are speaking about instantiation time, it's possible using the following syntax:
public class Outer {
public class Inner {}
}
...
Outer o = new Outer();
Outer.Inner i = o.new Inner();
However, it's not possible (without setAccessible(true)) to associate the existing instance of inner class with the other instance of outer class, because the field pointing to the enclosing instance is final:
javap Outer$Inner
Compiled from "Outer.java"
public class Outer$Inner extends java.lang.Object{
final Outer this$0;
public Outer$Inner(Outer);
}
You should be able to, using reflection.
Just get all fields of the inner class (getClass().getDeclaredFields())and see which field holds the parent, then change it (using field.set(innerInstance, newParent). Before that you should make the field accessible - setAccessible(true))
Since the field appears to be final, you may take a look at this article to see how to circumvent that.
That said, you shouldn't need to do this at all - it would be a double ugly hack for no actual gain.