Is there a way to parameterize a superclass with a static member class of the subclass?
Contrived Example
ExampleSuperClass.java:
package foo;
public class ExampleSuperClass<T> {
protected T field;
public ExampleSuperClass(T field) {
this.field = field;
}
public T getField() {
return field;
}
}
ExampleSubClass.java:
package foo;
public class ExampleSubClass extends ExampleSuperClass<Member> {
static class Member {
}
public ExampleSubClass() {
super(new Member());
}
}
Compilation fails on ExampleSubClass.java with error:
[javac] ExampleSubClass.java:3: error: cannot find symbol
[javac] public class ExampleSubClass extends ExampleSuperClass<Member> {
[javac] ^
[javac] symbol: class Member
[javac] 1 error
or in Eclipse with:
Member cannot be resolved to a type
in Eclipse the super invocation also has the error:
The constructor ExampleSuperClass(Member) refers to missing type Member
It works fine (aka no errors) if ExampleSubClass is instead parameterized with another package-protected top-level class.
Summary
The driving force behind this is that I have a generic super class and many different ${SubClass-extends-GenericSuperClass}.java and ${ClassUsedBySubClass}.java pairs. But since ClassUsedBySubClass is only ever referenced by SubClass, it would be nice to:
restrict ClassUsedBySubClass's access by making it a static member class and
cut down on the number of files by not giving ClassUsedBySubClass its own file.
So, is there a way to use a subclass's member class in parameterizing the superclass?
If there isn't -- is there an alternative approach?
Yes, you can do it. However, since Java uses the scope outside the declaration for name resolution, you must qualify Member with the name of ExampleSubClass:
public class ExampleSubClass extends ExampleSuperClass<ExampleSubClass.Member> {
...
}
Because Member is an inner class, you need to specify that when using it as a generic type. I'm able to get it to compile by using the following:
static class ExampleSubClass extends ExampleSuperClass<ExampleSubClass.Member> {
static class Member {
}
public ExampleSubClass() {
super(new Member());
}
}
Member is a static nested class. Static nested class is accessed using the enclosing class name:
ExampleSubClass.Member
You do this every time a static nested class occurs outside the scope of its outer class.
Therefore, the following is correct:
public class ExampleSubClass extends ExampleSuperClass<ExampleSubClass.Member> {
^
enclosing class name
static class Member {
...
}
}
Same as when you want to create an object for the static nested class:
ExampleSubClass.Member member = new ExampleSubClass.Member();
Related
File AbstractContainer.java
package container;
import static container.AbstractContainer.*;
public abstract class AbstractContainer<ElementType extends AbstractElement> {
public static abstract class AbstractElement {
}
}
File ConcreteElement.java
package container;
import static container.ConcreteContainer.*;
import static container.AbstractContainer.*;
public class ConcreteContainer extends AbstractContainer<ConcreteElement> {
public static class ConcreteElement extends AbstractElement {
}
}
This code gives me a compile error:
java: type argument container.ConcreteContainer.ConcreteElement is not within bounds of type-variable ElementType
but the IDE doesn't see any problems (IDEA 12).
First: What is going on here?
Second question, In AbstractContainer.java why do I have to static import the nested class, that's obviously in scope, to use it in the generic type (extends AbstractElement instead of extends AbstractContainer.AbstractElement) ?
First, you don't have to static-import the class. Just qualify the reference to your inner classes:
public abstract class AbstractContainer<ElementType extends
AbstractContainer.AbstractElement> {
and
public class ConcreteContainer extends
AbstractContainer<ConcreteContainer.ConcreteElement>
When compiling with Java 1.6, I don't see your compiler error. But I do see it when compiling with 1.7.
As it happens, fully-qualifying AbstractElement when declaring ConcreteElement seems to satisfy the compiiler:
public class ConcreteContainer extends
AbstractContainer<ConcreteContainer.ConcreteElement> {
// fully qualify here
public static class ConcreteElement extends AbstractContainer.AbstractElement {
}
}
First question - probably a compiler bug.
Second question - probably due to technicality:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.3
The scope of a declaration of a member m declared in or inherited by a class type C (ยง8.1.6) is the entire body of C
For example
#SomeAnnotation(M.class)
class C
<T extends M>
{
static class M{}
}
the code is illegal, because M is used outside the body of C, in annotation, and in type parameter.
Prior to Java 5, there's no annotation/generics, therefore "the entire body of C" covers all places "M" can be sensibly referenced. But now the rule is outdated, we should really extend the scope of M a little bit; I don't see any problem in doing that.
I have abstract java class which contains nested class declared as protected static:
public abstract class AbstractJavaClass {
// contains nested class
protected static class InnerClass {
...
}
}
When I'm trying to instantiate it in Scala class extending AbstractJavaClass like I do in java (i.e. just referring name inside method new InnerClass):
class ScalaClass extends AbstractJavaClass {
def method = new InnerClass()
}
I get error: type InnerClass is not a member of ScalaClass. What am I doing wrong? The same in java works fine.
Stumbled here: access java base class's static member in scala
It says
This isn't possible in Scala. Since Scala has no notation of static you can't
access protected static members of a parent class. This is a known limitation.
Though I couldn't resolve initial issue (when class is protected static), when I changed it to public static in definition of AbstractJavaClass I could use it this way:
class ScalaClass extends AbstractJavaClass {
import AbstractJavaClass.InnerClass
def method = new InnerClass
}
Using new InnerClass without import gives the same error.
Here is what I'm trying to accomplish
File 1: ./net/Class1.java
package net;
public class Class1
{
protected static class Nested
{
}
}
File 2: ./com/Class2.java
package com;
import net.Class1;
public class Class2 extends Class1
{
Nested nested = new Nested();
}
Here is the error I'm getting
>javac ./net/Class1.java ./com/Class2.java
.\com\Class2.java:7: error: Nested() has protected access in Nested
Nested nested = new Nested();
Is this error expected? Am I doing something wrong?
Problem
Few important facts (which many people forget or are not aware of):
default constructors (including ones for static and non-static nested classes) have same visibility as visibility of class which they belong to. So in case of protected class Nested{...} its default constructor is also protected.
element with protected visibility can be accessed only from class which
belongs to same package as class which declared that element,
extends (explicitly or implicitly) class which declared it.
Your Class2 extends Class1 so it only have access to members of Class1 (including access to Nested type). But since it
doesn't extend Nested (even implicitly, it only inherits access to it since it is protected)
doesn't belong to same package as Nested
it can't access protected elements from Nested class (including constructors).
Solution:
To solve that problem make Nested constructor public by either
explicitly creating no-argument constructor of Nested class with public modifier:
package net;
public class Class1 {
protected static class Nested {
public Nested(){
//^^^^^^
}
}
}
making Nested class public (its default constructor will also become public - see point 1.)
package net;
public class Class1 {
public static class Nested {
//^^^^^^
}
}
The following Java program just calculates the area of a circle. It uses the concept of inner classes available in Java. One of the inner classes (FirstInner) inherits it's enclosing class named Outer and the SecondInner class derives the FirstInner in turn. The program is working just fine. There is no problem at all. Let's have look at it.
package innerclass;
import innerclass.Outer.SecondInner; // Need to be inherited.
import java.util.Scanner;
class Outer
{
protected double r;
public Outer()
{
}
public Outer(double r)
{
this.r=r;
}
public class FirstInner extends Outer
{
public FirstInner(double r)
{
super(r);
}
}
final public class SecondInner extends FirstInner
{
public SecondInner(double r)
{
Outer.this.super(r); //<-------------
}
public void showSum()
{
System.out.print("\nArea of circle = "+(Math.pow(r, 2)*Math.PI)+"\n\n");
}
}
}
final public class Main
{
public static void main(String[] args)
{
Scanner s=new Scanner(System.in);
System.out.print("\nEnter radius:->");
double r=s.nextDouble();
Outer o=new Outer();
SecondInner secondInner = o.new SecondInner(r);
secondInner.showSum();
}
}
Now in the SecondInner class, I'm qualifying it's super class which is the FirstInner class first with this and again with Outer like Outer.this.super(r); which simply looks like just super(r);.
The use of only super(r) rather than Outer.this.super(r); causes a compiler-time error indicating that "cannot reference this before supertype constructor has been called". Why is it so? I mean why I have to use Outer.this.super(r); rather than just super(r)?
One more point when I make the FirstInner class static, the program issues no compile-time error and allows to use just super(r) in place of Outer.this.super(r);. Why?
I get a different error from my Eclipse environment:
"No enclosing instance of type Outer is available due to some intermediate constructor"
This one is clearer and can be linked to the fact that you cannot instantiate a non-static inner class before the outer class has been instantiated.
Please see the example that is described here.
15.11.2 Accessing Superclass Members using super
From the java tut
http://download.oracle.com/javase/tutorial/java/javaOO/nested.html
An instance of InnerClass can exist only within an instance of
OuterClass and has direct access to the methods and fields of its
enclosing instance.
Going by that statement, the following approach makes sense. You are accessing the instance using "this" by resolving with the help of the class name, which is defined here
in primary expressions .
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20860
class SecondInner extends FirstInner
{
public SecondInner(double r)
{
Outer.this.super(r); //<-------------
}
public void showSum()
{
System.out.print("\nArea of circle = "
+(Math.pow(r, 2)*Math.PI)+"\n\n");
}
}
}
For example if your SecondInner were to be declared within FirstInner it has to be accessed using FirstInner.this.super()
I can create Main class with Access specifier "public" or default.
But why can't I create with protected. As default itself allowed why not protected.
Public:
public class MainClass {
public static void main(String[] args) {
}
}
Default:
class MainClass {
public static void main(String[] args) {
}
}
Protected:
protected class MainClass {
public static void main(String[] args) {
}
}
Its showing error:
Illegal modifier for the class MainClass; only public, abstract & final are permitted MainClass.java SCJP/src line 1 Java Problem
protected relates to giving subclasses of the containing type access to a member. There's no containing type here, so what would it mean?
Note that this has nothing to do with main as such... it applies to any top-level class. It is valid for a nested type to be protected though:
public class Foo {
protected static class Bar{}
}
This allows subclasses of Foo to access Bar.
protected or private class has no sense - as a top level class, not the inner one. Such class wouldn't be usable. Protected mean, that elements in the class can be visible by children. But what if the whole class would be package. In this case any other class could even see such class (even in the same package) until it extends such class. It would be strange and that is why it is forbidden.
Aren't you compromising on the visibility of your main method by having the enclosing class as protected?
As far as I know, for a main method to be accessible there are two things you need to ensure:
The enclosing class is public. (I think it can be default also)
The enclosing class and the parent file have the same name.
Hope this helps :)