51

My coworker suggested making several of the Eclipse code-formatting and warning settings to be more rigorous. The majority of these changes make sense, but I get this one weird warning in Java. Here's some test code to reproduce the "problem":

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();   // !!!
        this.anInstance.doSomething();
    }
}
// using "this.anInstance" instead of "anInstance" prevents another warning,
// Unqualified access to the field WeirdInnerClassJavaWarning.anInstance    

The line with the !!! gives me this warning in Eclipse with my new warning settings:

Access to enclosing constructor WeirdInnerClassJavaWarning.InnerClass() is emulated by a synthetic accessor method. Increasing its visibility will improve your performance.

What does this mean? The warning goes away when I change "private static class" to "protected static class", which makes no sense to me.


edit: I finally figured out the "correct" fix. The real problem here seems to be that this nested private static class is missing a public constructor. That one tweak removed the warning:

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
        public InnerClass() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();
        this.anInstance.doSomething();
    }
}

I want the class to be a private nested class (so no other class can have access to it, including subclasses of the enclosing class) and I want it to be a static class.

I still don't understand why making the nested class protected rather than private is another method of fixing the "problem", but maybe that is a quirk/bug of Eclipse.

(apologies, I should have called it NestedClass instead of InnerClass to be more clear.)

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • I do not understand a point: what's the usefulness of having a inner class that can't be used by anybody else? I think I'll put the class in a separate class file and let the other programmers choice if it's useful for other scopes or not. – Marco Sulla Jun 27 '19 at 12:41

6 Answers6

46

You can get rid of the warning as follows:

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass {
        protected InnerClass() {}  // This constructor makes the warning go away
        public void doSomething() {}
    }

    final private InnerClass anInstance;
    {
        this.anInstance = new InnerClass(); 
        this.anInstance.doSomething();
    }
}

As others have said, Eclipse is complaining because a private class with no explicit constructor cannot be instantiated from outside, except via the synthetic method that the Java compiler creates. If you take your code, compile it, and then decompile it with jad (*), you get the following (reformatted):

public class Test {
  private static class InnerClass {
    public void doSomething() {}
    // DEFAULT CONSTRUCTOR GENERATED BY COMPILER:
    private InnerClass() {}

    // SYNTHETIC METHOD GENERATED BY THE JAVA COMPILER:    
    InnerClass(InnerClass innerclass) {
      this();
    }
  }

  public Test() {
    anInstance.doSomething();
  }

  // Your instance initialization as modified by the compiler:
  private final InnerClass anInstance = new InnerClass(null);
}

If you add a protected constructor, the synthetic code is unnecessary. The synthetic code is theoretically, I suppose, slower by a minescule amount than non-synthetic code using a public or protected constructor.

(*) For jad, I linked to a Wikipedia page ... the domain that hosted this program has expired, but Wikipedia links to another that I have not tested myself. I know there are other (possibly more recent) decompilers, but this is the one I started using. Note: It complains when decompiling recent Java class files, but it still does a good job.

Eddie
  • 53,828
  • 22
  • 125
  • 145
  • I'm assuming you were in the process of posting before I made my edit. In any case this does seem to be the right answer. – Jason S May 28 '09 at 14:40
  • Yes, you and I were typing at the same time. I've extended my answer to also explain why you get the Eclipse warning. – Eddie May 28 '09 at 14:46
  • +1 about the decompiler, I never knew there was such a thing. It sounds like I should submit a bug for Eclipse that they should change the text for this warning so that it's a little clearer what this means and what the fix should be. – Jason S May 28 '09 at 14:51
  • 5
    The best solution, unless this is a J2ME project, is to disable the warning in Eclipse and write your code to match your intent. The performance hit will be either negligible or zero after JIT. – Darron May 28 '09 at 14:54
  • I added a link to jad. (Sort of; the original page hosting jad expired months back.) The best advice is probably that of Darron and others. For any modern compiler, the performance impact will be very small. Write code to your intent (which may well be the protected constructor) and don't worry about micro-optimizations ... J2ME perhaps excepted. – Eddie May 28 '09 at 15:07
  • @Darron: thanks -- Eddie's solution is my intent. I had inadvertently got into the habit of leaving out null constructors for some of my classes. The negligible performance hit isn't really the issue, rather it's about getting meaningful warnings when appropriate and knowing how to address them. – Jason S May 28 '09 at 15:14
  • 1
    @Darron & Co. I don't see where a performance should come from anyway. The compiler generates the synthetic method, so at run time it should look just like when you put it there yourself. Or am I missing something? – Jens Schauder Jul 25 '12 at 08:56
  • Just compile with 'javac -d tmp -XD-printflat WeirdInnerClassJavaWarning.java' to see the synthetic code, no need for jad – Joseph Lust Aug 21 '13 at 15:18
  • Any security implications of this warning ? – Mr_and_Mrs_D Oct 17 '13 at 15:46
  • I am not aware of any security implications. – Eddie Oct 23 '13 at 23:16
21

By the way, the setting to turn the warning off is in the Java Errors/Warnings page under "Code style" and is called:

Access to a non-accessible member of an enclosing type

robinst
  • 30,027
  • 10
  • 102
  • 108
9

You can not instantiate InnerClass from WeirdInnerClassJavaWarning. It's private, JVM wouldn't let you to, but the Java language (for some reason) would.

Therefore, javac would make an additional method in InnerClass that would just return new InnerClass(), therefore permitting you to create InnerClass instances from WeirdInnerClassJavaWarning.

i don't think you really need to get rid of it because the perfomance drop would be inmeasureably tiny. However, you can if you really want to.

alamar
  • 18,729
  • 4
  • 64
  • 97
  • 2
    +1 the performance improvrement should be completely insignifant on a modern JVM. Eclipse is just being over-enthusiatic here, I'd turn that warning off completely. – skaffman May 28 '09 at 14:22
  • "You can not instantiate InnerClass from WeirdInnerClassJavaWarning. It's private, JVM wouldn't let you to, but the Java language (for some reason) would." There's no constructor present and the Java language will automagically create a no-arg public constructor is none are present. That this magic is going is what Eclipse was warning about. – Powerlord May 28 '09 at 14:50
  • 1
    I guess that this constructor would be private in case of a private inner class. Otherwise this warning doesn't make any sense. – alamar May 28 '09 at 20:04
4

I still don't understand why making the nested class protected rather than private is another method of fixing the "problem", but maybe that is a quirk/bug of Eclipse

That is not a quirk/bug of Eclipse, just a feature of Java. The Java Language Specification, 8.8.9 says:

... if the class is declared protected, then the default constructor is implicitly given the access modifier protected ...

user85421
  • 28,957
  • 10
  • 64
  • 87
  • ...and protected and private have different implications for enclosing classes? I thought the only difference was how access is granted to *subclasses*. – Jason S May 28 '09 at 15:12
  • 1
    It has no implications at source code level (compiler) but at the byte code level (virtual machine). AS long as I know, the JVM does not know nested classes, that's why you get an extra .class file for them. The compiler must create synthetic methods so the "outer" class can access the private members of the nested class (at runtime). (BTW: protected also includes package access, which would be sufficient to avoid the warning) (BTW2: as defined by the JLS, static nested classes are not inner classes...) – user85421 May 28 '09 at 15:47
3

To help folks out, here is what you get if you use the original class code in the question with

javac -XD-printflat WeirdInnerClassJavaWarning.java -d tmp

Raw output, compiler added the comments. Note the addition of the synthetic package private class and constructor.

public class WeirdInnerClassJavaWarning {
    {
    }

    public WeirdInnerClassJavaWarning() {
        super();
    }
    {
    }
    private final WeirdInnerClassJavaWarning$InnerClass anInstance;
    {
        this.anInstance = new WeirdInnerClassJavaWarning$InnerClass(null);
        this.anInstance.doSomething();
    }
}

class WeirdInnerClassJavaWarning$InnerClass {

    /*synthetic*/ WeirdInnerClassJavaWarning$InnerClass(WeirdInnerClassJavaWarning$1 x0) {
        this();
    }

    private WeirdInnerClassJavaWarning$InnerClass() {
        super();
    }

    public void doSomething() {
    }
}

/*synthetic*/ class WeirdInnerClassJavaWarning$1 {
}
Joseph Lust
  • 19,340
  • 7
  • 85
  • 83
0

You should be able to get rid of it by using the default scope instead of private or protected, i.e.

static class InnerClass ...

It's also worth noting that my placing your cursor on the line of code with the warning and pressing ctrl-1, Eclipse may be able to fix this automatically for you.

izb
  • 50,101
  • 39
  • 117
  • 168
  • thanks but I don't want that, it should be nonvisible to classes other than the enclosing class. – Jason S May 28 '09 at 14:35
  • 1
    You might then want to suppress the warning in this case since Eclipse's performance-oriented warning is irrelevant to your needs. – izb May 28 '09 at 14:53
  • The relevant config being "Access to a non-accessible member of an enclosing type" – akauppi May 06 '14 at 10:34