Can an interface nested inside a class be non-static? - java

I'm reading about some articles[1][2] online about nested interface in Java, I understand that
interface A {
...
interface B { // this is static by default
...
}
}
But I'm not sure that
class C {
...
interface D { // Is this static by default? Why?
...
}
}
In short, is that "a nested interface is always static" true?
[1] https://beginnersbook.com/2016/03/nested-or-inner-interfaces-in-java/
[2] https://www.programcreek.com/2013/08/inner-interface-in-java/

a non-static inner class is just syntax sugar. A non-static inner class is exactly the same as a standard 'outer' class, with one exception: It has an invisible field of the type of your outer class which is declared final. ALL constructors of your inner class have as first parameter the instance of the outer to which this field must be set.. and then there's a biiig sack of syntax sugar thrown all over this to also hide those.
But that's really how it works, and you can use javap to confirm it. So, given:
public class Outer { public class Inner {} }
versus:
public class Outer {}
class Desugared {
private final Outer outer;
public Desugared(Outer outer) { this.outer = outer; }
}
these things are the same except for syntax:
Outer o = new Outer();
o.new Outer.Inner();
is the same as:
Outer o = new Outer();
new Desugared(o);
etcetera.
Here's the thing: interfaces do not have fields.
Given that they don't, they cannot have this hidden field. And therefore they cannot be 'non-static'. They are therefore implicitly 'static' (they do not have an instance of the outer class implicitly available to them), and you can't change that.

Yes, your "in short" is always true. Quoting directly from the language spec:
A member interface is implicitly static(§9.1.1). It is permitted for the declaration of a member interface to redundantly specify the static modifier.
where "member interface" is defined shortly before:
A member interface is an interface whose declaration is directly enclosed in the body of another class or interface declaration (§8.1.6, §9.1.4).

Classes might be instantiated, interfaces can not be instantiated.
Meaning: when you have an inner class, you might or might not want to create an instance of that inner class with an "outer" object or not.
Assume you have:
class X {
... class Y {
then both someInstanceOfX.new Y() and new X.Y() might make sense.
But when you have an inner interface, the only use of that interface would be with some other class going implements C.D. There is no meaningful way to access C.D that would suggest that D should not be static.
Just as your second link says:
Because an interface can not be instantiated, the inner interface only makes sense if it is static.

Related

Do anonymous classes always have to exist elsewhere?

I've been trying to understand the difference between nested, local, anonymous classes and I'm confused about a few things, but mostly why you cannot do this:
class OuterClass {
InnerClass innerClass = new InnerClass() {
//DO SOMETHING
};
}
w/o doing this...
class OuterClass {
class InnerClass {
}
InnerClass innerClass = new InnerClass() {
//DO SOMETHING
};
}
or this...
class OuterClass {
InnerClass innerClass = new InnerClass() {
//DO SOMETHING
};
}
class InnerClass {
}
How is this at all different from this really?
class OuterClass {
InnerClass innerClass = new InnerClass();
innerClass.innerClassMethod();
}
class InnerClass {
public void innerClassMethod() {
}
}
And why is it called anonymous? I dont understand that. Reading this link here I understand the logistical differences (i.e. no name, only 1 instantiation, only accessible where defined, etc. But it's really not a class so why is it called a class? It's actually, from what I can see an instantiation of a class. I see you can have other methods as well from the class it is derived from, but how are you allowed to have completely different methods than the base class w/o having to use #Override?
Everywhere you have written 'DO SOMETHING' you are actually creating a new class that inherits from whatever type is behind the 'new' Keyword...
This temporary class has no name of its own, hence anonymous.
It ends with ; because it is actually just a parameter of the outer class.
As Silvio pointed out in the comments all classes extend some class, even if it just the Object class.
You can't do this:
class OuterClass {
InnerClass innerClass = new InnerClass() {
//DO SOMETHING
};
}
Because InnerClass does not exist yet to inherit from.
Two points about that what you're doing there with InnerClass innerClass = new InnerClass() {};
First you're using the class InnerClass as a template that you extend by another anonymous class, instantiate that and save that reference to the variable innerClass.
So InnerClass has to be defined.
And because there is no available named class definition for the object named innerclass, this is why it's anonymous.
Second, your comment //DO SOMETHING in there is wrong. The only thing you can do in there is to override already existing methods. Yes, you can also add new methods, but you cannot call the from the outside directly.
InnerClass could be a normal class, an abstract class, or an interface.
Defining class InnerClass {} inside another class would be a nested class. The way you do it it is dependent on the state of the OuterClass.
This could also be static, i.e. decoupled from any instance that Outerclass and its generica parameters would have.
Your third example is a normal (additional) class definition inside a file. But because Java Language Specification defines that there can not be two top-level public classes in one file, this second class definition cannot be public. Because JLS also states that the one top-level public class inside a file has to have the same name as the file it's defined in.
And to answer your last question, in reference to what I said about 'not being able to acces other methods': In your last example you explicitly define the method innerClassMethod() in a names class, and so it can be accessed.

Anonymous inner class at member level

In the book 'Java OCP 8 Programmer II Study Guide', it is said that
an anonymous inner class is a local inner class
and
a local inner class is a nested class defined within a method
However, I am able to define an anonymous inner class outside a method:
public class Outer {
Foo ex = new Foo {
#Override
public void bar() {
System.out.println("This is my bar implementation");
}
}
}
void TestClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.ex.bar();
}
}
Is the book wrong in saying that an anonymous inner class is a local inner class as it doesn't have to be local (within a method) or is the example I provided not an anonymous inner class (as it is assigned to a named variable)?
Thanks
ex is not a local class. anything that a local class cannot be anonymous and vice-versa (to the best of my knowledge)
Quoting from Oracle's Java OO tutorial on Anonymous Classes :
Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.
Couple of lines below ..
While local classes are class declarations, anonymous classes are expressions, which means that you define the class in another expression

Fundamental impediments for non-static access from inner classes to outer interfaces

Example:
interface Outer {
default String get() {
return "hi";
}
class Inner {
String got() {
return get();
}
}
}
This yields the error
java: non-static method get() cannot be referenced from a static context.
The inner interface/class is always static; unlike with an outer class where it's non-static unless declared static.
This is how things are today and in the upcoming java 8. Is there a fundamental reason for this difference between outer classes and outer interfaces?
Update:
After reading #Radiodef´s comment I changed the inner interface to be an inner class. An outer class can't contain a non-static inner interface so the example was confusing. An inner class is really what I would like anyway.
Update: For reference. This is perfectly legal:
class Outer {
String get() {
return "hei";
}
class Inner {
String got() {
return get();
}
}
}
Maybe I misunderstood your question, but your code snippet is exactly equivalent to
interface Outer {
public default String get() {
return "hi";
}
public static class Inner {
String got() {
return get();
}
}
}
As the JLS Chapter 9.5 (Java 8) states
A member type declaration in an interface is implicitly public and
static. It is permitted to redundantly specify either or both of these
modifiers.
So if you did
Inner innerInstance = new Outer.Inner();
innerInstance.got();
what would get() be invoked on? There is no object of type Outer involved here.
Is there a fundamental reason for this difference between outer
classes and outer interfaces?
This isn't the issue. Your class code is an example of inner classes, ie. non static nested class. The interface code is an example of a static nested class. You are comparing two different things.
The equivalent example with a static nested class within an enclosing class would be
class Outer {
String get() {
return "hei";
}
public static class Inner {
String got() {
return get(); // won't compile
}
}
}
where again it doesn't make sense for get() to work since there is no corresponding (enclosing) instance to invoke it on.
If the question, as #Radiodef put it, is
why must the class be implicitly static beyond that this is the
existing spec?
then my answer is the following:
An interface, by definition, is
A point at which independent systems or diverse groups interact
An interface does not have state and it does not have behavior. It simply describes behavior. Interface members are implicitly static because an interface does not have state.
I will provide a way for you to remember it.
For a static member, it's not bound to an object of the declaring class/interface; for a non-static member, it has to bound to an object of the declaring class.
All interface's members are implicitly static, except default ones.
So, in your Exemple: static class Inner is not bound to an object of interface Outer(Since there is no meaning of object of interface), so it can't call the member of Outer.
For your legal one in the Update: class Inner is bound to an object of class Outer, so when you create an object of Inner, you also create an anonymous object of class Outer, so when you call get() in Inner::got(), the get() is called upon that anonymous object.
Hope this help.

Regarding inner classes private member variable access

Say there is an class named MyOuter which consists of a simple inner class named MyInner. In trying to learn how inner classes work, I'm trying to understand whether an outer class private member variable is accessible from the inner class itself or not.
class MyOuter {
private int x = 7;
// inner class definition
class MyInner {
public void seeOuter() {
System.out.println("Outer x is " + x);
}
} // close inner class definition
} // close outer class
As per my analysis, the preceding code is perfectly legal. Notice that the inner class is indeed accessing a private member of the outer class. That's fine, because the inner
class is also a member of the outer class. So just as any member of the outer class
(say, an instance method) can access any other member of the outer class, private
or not, the inner class (also a member) can do the same.
Please advise whether my reason was correct or not.
an inner class is a member of its enclosing class and has direct access to that object's methods and fields. for more information, please see Nested Classes.

How to make an outer class inherited from an inner class?

How can I make something like this work:
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
}
}
class OuterExtendsInner extends Outer.InnerBase {
OuterExtendsInner(Outer o) { o.super(); }
void method() {
// How do I use some_member here?
// Writing Outer.this.some_member -> error about Outer not being an enclosing class
// Writing just some_member -> no-go, either
}
}
The workaround is to have a method in InnerBase that returns Outer.this and call THAT from derived classes, but is there another way?
I primarily want to extend the InnerBase from outside in order to have better code-organization, but I could move all derived classes into Outer.
The problem here is that the synthetic field which links InnerBase to Outer is a private field. Thus, we can only access the outer object from within InnerBase, or if some method or field there provides a reference to the same object.
You could do this in OuterExtendsInner:
class OuterExtendsInner extends Outer.InnerBase {
Outer o;
OuterExtendsInner(Outer o) {
o.super();
this.o = o;
}
void method() {
// now you can reference o.some_member
int x = o.some_member;
}
}
The answer is: you can't, because it would break encapsulation. Only InnerBase can have access to attributes of Outer, not OuterExtendsInner. It is not direct inheritance. InnerBase does not inherit of Outer.
I haven't tried WhiteFang34's answer. It might work, but I'm not clear on it ...
If you really want to define an extension of your inner class elsewhere than in the outer class, the most natural thing would be to define it as an extension of the inner class in another outer extending your outer class as follows:
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
}
}
class OuterExtendsOuter extends Outer {
class InnerExtendsInner extends Outer.InnerBase {
void method() {
System.out.println(some_member);
}
}
}
I haven't actually run this code either, but it should work.
Update:
Based on the comment thread, I have now compiled and run both my code above and WhiteFang34's code.
Both in fact work, but as noted in the comments by Paŭlo Ebermann, both create two copies of the outer inside the instantiated inner class.
I'm going to upvote Paŭlo's answer, and would advocate just not trying to do this by either tactic, as it's really an abuse of the inner class mechanism.
Just make your extended inner classes live inside the same outer class!
Update 2:
What happens in my code, based on runtime examination using a debugger and on examining the output from javap inspections of the classes, is that both InnerBase and OuterExtendsOuter$InnerExtendsInner have synthetic private final fields named this$0. Because no constructors are explicitly defined, the default constructors are used, and the code snippet
OuterExtendsOuter outer = new OuterExtendsOuter();
Outer.InnerBase inner = outer.new InnerExtendsInner();
causes these two fields to both reference outer.
In other words, Paŭlo's comment is entirely correct.
By further experimentation, the same actually happens if you extend InnerBase in another inner class of Outer, so it has little to do with it being defined in the same outer class or an extension of it, but is in fact an outcome of how non-static inner classes are handled generally.
I suspect this is documented somewhere, but I haven't seen that.
Probably best to mix inheritance and inner classes as little as possible!
Just have a getter method in the InnerBase?
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods.
{
return some_member;
}
}
}
class OuterExtendsInner extends Outer.InnerBase {
OuterExtendsInner(Outer o) { o.super(); }
void method() {
// you can access "some_member" now
int myNumber = getSome_Member();
}
}
Well your problem is that every instance of InnerBase (I know it's abstract) has to have a reference to an Outer object. That is part of the semantics of nested classes. Instantiating OuterExtendsInner would need such a reference.
You can avoid that making InnerBase a static nested class.
The outer class can extend the inner class iff the inner class is compiled to ".class".
Now, every time you compile the outer class it encounters the "extends innerclass" which is
not yet compiled and the compiler will throw a NoClassDefException or ClassNotFoundException.
Isn't it ? So you will never get that inner class compiled. If you can overcome this problem
then you can also extend the inner class :) .

Categories