I recently bumped into a rare -- but perfectly legal syntax: Local Classes.
I was wondering why can't I access a local class outside that method? With what is it different from an inner class which can be access in the outer class or with an enclosed object (outer.new Inner())?
Example: this is perfectly valid,
//this is valid
public class Outer {
int outer_x = 100;
public void test() {
Inner inner = new Inner();
inner.display();
}
public class Inner {
public void display() {
System.out.println("Outer x is: " + outer_x);
}
}
}
This is valid as well
//this is valid as well
public class Outer {
int outer_x = 100;
public void test() {
Inner inner = new Inner();
inner.display();
}
public class Inner {
public void display() {
System.out.println("Outer x is: " + outer_x);
}
}
public void test2() {
Inner inner2 = new Inner();
inner2.display();
}
}
But this will not compile:
public class Outer {
int outer_x = 100;
public void test() {
class Inner {
public void display() {
System.out.println("Outer x is: " + outer_x);
}
}
Inner inner = new Inner();
inner.display();
}
public void test2() {
Inner inner2 = new Inner(); // error here
inner2.display();
}
}
Why is this so?
Because every call to test() creates a complete new version of the class. Therefore it may access (final or effectively final) local variables!
Here's a rundown what's going on.
public class Outer {
public void test(int i) {
class Inner {
private int x = i; // i may be different on each call of test
public void display() {
System.out.println("Inner x is: " + x);
}
}
Inner inner = new Inner();
inner.display();
}
public void test2() {
test(1); // prints 1
test(2); // prints 2
//now imagine this is valid
Inner inner2 = new Inner();
inner2.display();// what's the value of x??
}
}
Related
I want to call the Inner class method in outer class, but it isn't working. I also made a reference of Inner class object and try to call the method, but also invalid again
class Outer{
int a;
public void show() {
System.out.println("Show Method");
}
Inner obj=new Inner();
obj.display();
class Inner{
public void display() {
System.out.println("Display Method");
}
}
}
It is very unclear what you are trying to do, however, the syntactically smallest possible change you can make to get your code to compile (any maybe do what you want, although it is not clear to me what it is that you are trying to achieve), would be to move the method call into an instance initializer:
class Outer {
int a;
public void show() {
System.out.println("Show Method");
}
Inner obj = new Inner();
{ obj.display(); }
// ↑ ↑
class Inner {
public void display() {
System.out.println("Display Method");
}
}
}
Now, given a suitable program entry point:
class Test {
public static void main(String... args) {
new Outer();
}
}
This will print:
Display Method
I am just learning Java concepts.
Can anyone let me know why i am not able to run this program?
package innerClasses;
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
class Inner {
public void m2() {
System.out.println(i);
}
}
}
public static void main(String[] args) {
Test t = new Test();
Test.Inner in = t.new Inner();
t.m1();
}
}
Can anyone let me know why i am not able to run this program?
The most basic reason is because of scope. In order to do
Test.Inner in = t.new Inner();
Inner must be defined in Test, but it is instead defined in m1 scope.
The class Inner is declared inside the method m1(), what makes it not available outside this method.
Your code has to look like the following to be able to run, although it will not print anything...
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
}
class Inner {
public void m2() {
System.out.println(i);
}
}
public static void main(String[] args) {
Test t = new Test();
Test.Inner in = t.new Inner();
t.m1();
}
}
Replacing t.m1(); by in.m2(); will output 10.
EDIT
In case you have to create the inner class inside the method, make it like
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
class Inner {
public void m2() {
System.out.println(i);
}
}
// this is what makes it run
Inner myInner = new Inner();
myInner.m2();
}
public static void main(String[] args) {
Test t = new Test();
t.m1();
}
}
to compile and run.
IMHO this is not a good way to go...
A method inner class is only visible to that method only so you can't use this at any other location.
for using this class you have to declare it outside of the method.
You cannot compile it as the scope of Inner class is the m1 method.
If you want to be able to create instances of Inner class you can define it directly inside the Test:
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
}
// On the Test class level
class Inner {
public void m2() {
System.out.println(i);
}
}
public static void main(String[] args) {
Test t = new Test();
Test.Inner in = t.new Inner();
t.m1();
in.m2(); // prints 10
}
}
Another feature provided by Java is to use anonymous classes. It can be created by implementation of some Interface:
// Define an interface to be able to implement it inside the method
interface Inner {
void m2();
}
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
// Implement interface and call method on it
// Functional style:
((Inner) () -> System.out.println(i)).m2();
// Legacy style:
new Inner() {
#Override
public void m2() {
System.out.println(i);
}
}.m2();
}
public static void main(String[] args) {
Test t = new Test();
t.m1(); // prints 10 twice
}
}
or extending some class:
// A class we going to extend
class Inner {
void m2() {
System.out.println(11);
}
}
public class Test {
int i=10;
static int j=20;
public void m1() {
int k=30;
final int m=40;
// Extend inner class and call method on it
new Inner() {
void m2() {
System.out.println(i);
}
}.m2();
}
public static void main(String[] args) {
Test t = new Test();
t.m1(); // prints 10, not 11
}
}
So the best way for you depends on what code design do you want to get finally.
Your class Inner is what the JLS calls a Local Class (14.3. Local Class Declarations).
The scope of a Local Class is defined as (6.3. Scope of a Declaration):
The scope of a local class declaration immediately enclosed by a block (§14.2) is the rest of the immediately enclosing block, including its own class declaration.
The scope of a local class declaration immediately enclosed by a switch block statement group (§14.11) is the rest of the immediately enclosing switch block statement group, including its own class declaration.
In your case it is declared in a block, which is the body of your method. So the scope where your class is visible is the rest of this method body.
As the type Inner is not visible in main(), you cannot use it there. You could create instances of it and use them within m1(), though.
The very basic reason for compile time error is "Scope".
As per your code, class Inner is defined inside method m1 (this class is called Local class/method local class),so if you observe the scope of method variable, its within the declared method only and we cannot access any method variable outside that method and this is reason, the scope of class Inner is limited to the m1 method
so if you want to instantiate class Inner and invoke its methods then you must do it in m1 method (in code , you are trying to create class Inner object outside method m1, which is not possible) hence the code would be
public class Test {
int i = 10;
static int j = 20;
public void m1() {
int k = 30;
final int m = 40;
class Inner {
public void m2() {
System.out.println(i);
}
}
Inner in = new Inner();
in.m2();
}
public static void main(String[] args) {
Test t = new Test();
t.m1();
}
}
some more info , the local classes can be used when any repeated functionality is required inside a method (as nested methods are not allowed in java, so we can create inner class) and off course if we are not interested to create class level method for example
class Outer {
public void cal() {
class Inner {
public void sum(int x, int y) {
System.out.println("sum : "+(x+y));
}
}
Inner i= new Inner();
i.sum(10, 20); //sum is repeatdly needed in between code
i.sum(100, 200);
i.sum(1000, 2000);
i.sum(10000, 20000);
}
}
public class TestClass {
public static void main(String[] args) {
new Outer().cal();
}
}
public class MyClass {
int x=9;
public static void main(String args[]) {
MyClass myClass = new MyClass();
myClass.test();
}
public void test(){
int x=10;
class InnerClass{
int x = 11;
void print(){
int x = 12;
System.out.println(x);
System.out.println(this.x);
System.out.println(MyClass.this.x);
System.out.println("MyClass => test() => x :" + "?");
}
}
InnerClass innerClass = new InnerClass();
innerClass.print();
}
}
How to call MyClass test() method local variable x inside the InnerClass print() method. What i can write in place of ? in last System.out.println() method in order to get the value of test() x.
Unfortunately in Java you can't.
The only way to access the x in MyClass::test would be to rename both variables in your inner class and in your inner class method into something else.
There is no need though to rename the outer class field x as InnerClass::print would consider the variable in the most-inner scope.
Although this snippet is for demonstration purposes, better practice would have you have different and more significant names for each variable.
That works fine for me:
public class Outerclass {
public static void main(String args[]) {
Outerclass outer = new Outerclass();
outer.outerMethod();
}
// method of the outer class
void outerMethod() {
int num = 23;
// method-local inner class
class MethodInnerClass {
public void print() {
System.out.println("This is method inner class "+num);
}
}
// Accessing the inner class
MethodInnerClass inner = new MethodInnerClass();
inner.print();
}
}
To print outer class variable use
MyClass.this.x
Let's say I have the following code:
abstract class MyStream
{
public abstract Iterable<Integer> getIterable();
public MyStream append(final int i)
{
return new MyStream()
{
#Override
public Iterable<Integer> getIterable()
{
return cons(/*outer class's*/getIterable(), i);
}
};
}
public static Iterable<Integer> cons(Iterable<Integer> iter, int i) { /* implementation */ }
}
How can I reference getIterable of the outer class from the inner class with the same name?
MyStream.this should point to the inner class here, right? How to show an outer class with the same name?
If you call MyStream.this from the anonymous class it will point to the outer class so the code below should work as you expect:
return const(MyStream.this.getIterable(), i);
(if it did not you would get a StackOverflowError).
The reason why it works is that an anonymous class is an inner class.
Simplified example that prints outer 1:
public static void main(String args[]) {
MyClass c = new MyClass() {
#Override public String get() { return "outer"; }
};
System.out.println(c.append(1).get());
}
static abstract class MyClass {
public abstract String get();
public MyClass append(final int i) {
return new MyClass() {
#Override public String get() {
return cons(MyClass.this.get(), i);
}
};
}
public static String cons(String iter, int i) { return iter + " " + i; }
}
MyStream.this does not point to the inner class. The inner class is anonymous. It may confuse you because you used new MyStream() {...}, but in fact it's a new inner class, which has an internal name, and the new MyStream() {...} syntax simply serves to replace the SomeClass extends MyStream syntax.
It is worth noting that the inner class both extends MyClass and is nested in it. This means that both super and MyClass.this exist and are MyClass references, but they are two distinct object instances. super points to the inner class instance itself, but looks at it as if it was MyClass, while MyClass.this is the enclosing object.
class outer
{
public void outerfunction()
{
private class inner // Line-1
{
public void showinner()
{
System.out.println("I am in Inner Class");
}
}
}
}
Line-1 : This Line gives error.
I know if I write abstract or Final for class inner. then,it is fine. Why class inner can't be private ?
A private class is visible to all the methods and nested classes in the same file. Normally you would think of private as narrowing the scope of use, but in this case using private you are broadening the scope of where is the class is visible in a way Java doesn't support.
Making the method private is the same as doing this
class outer {
private class inner { // Line-1
public void showinner() {
System.out.println("I am in Inner Class");
}
}
public void outerfunction() {
}
}
Which looks fine until you consider that a nested class can use the values in it's outer context.
class outer {
public void printInner() {
// accessible in the same .java file because we made it private
inner i = new inner();
i.showInner(); // what should `x` be?
}
public void outerfunction(final int x) {
private class inner { // Line-1
public void showinner() {
System.out.println("I am in Inner Class, x= " + x);
}
}
}
}