8

I'm refactoring an android project which is getting to big. Running lint gives me the JSME issue Private member access between outer and inner classes. Considering the following example

public class Outer {
    private Inner mInner = new Inner();

    private class Inner {}
}

I get the information

Name
   private field Inner mInner

Location
   class Outer (default package)

Problem synopsis
   Access to private member of class 'Inner' at line 2

Problem resolution
   Make 'Inner' constructor package-local

Applying the problem resolution changes the source to

public class Outer {
    private Inner mInner = new Inner();

    private class Inner {
        Inner() {}
    }
}

I'm a little confused at the moment. Until now I thought the example would be equivalent to

public class Outer {
    private Inner mInner = new Inner();

    private class Inner {
        public Inner() {}
    }
}

Am I wrong in this case or is it an issue of lint?

tynn
  • 38,113
  • 8
  • 108
  • 143
  • Possible duplicate of [Should we declare a public constructor when the class is declared as package private?](http://stackoverflow.com/questions/243218/should-we-declare-a-public-constructor-when-the-class-is-declared-as-package-pri) – OneCricketeer Jan 08 '16 at 14:12
  • whenever you declare a class private, everything belonging to that class becomes private – awsome Jan 08 '16 at 14:19
  • @cricket_007 sadly no. It doesn't cover the part lint is complaining about. – tynn Jan 08 '16 at 14:19
  • I don't quite understand the problem, then. Having a `public` constructor vs a *package-level* (no access modifier) constructor shouldn't really matter since the class itself is `private`. E.g. no class outside of `Outer` can access `Outer.Inner`. The lint message seems to be saying that without a less restrictive constructor than `private`, you can't say `new Inner()` – OneCricketeer Jan 08 '16 at 15:49
  • 1
    Have a look at this answer. http://stackoverflow.com/a/15287730/2308683 – OneCricketeer Jan 08 '16 at 15:52

2 Answers2

4

Section 8.8.9 of the Java language specification, "Default constructor" says:

In a class type, if the class is declared public, then the default constructor is implicitly given the access modifier public (§6.6); if the class is declared protected, then the default constructor is implicitly given the access modifier protected (§6.6); if the class is declared private, then the default constructor is implicitly given the access modifier private (§6.6); otherwise, the default constructor has the default access implied by no access modifier.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
2

You're wrong in your understanding, but the linter is not being particularly clear, and the advice probably isn't relevant for Android (which isn't J2ME).

As David explained, the inner class's implicit default constructor has the same access modifier as the class itself, but private members are accessible within the same compilation unit (Java file). There's no language reason to avoid the private constructor.

However, internally, since the classes are compiled into separate output files, the compiler has to create synthetic adapter methods to provide the classes access to the private members. The runtime disadvantage of these methods is irrelevant for desktop applications, but for something as cramped as J2ME, the difference might be worth eliminating by making the member accessible directly (using package scope).

Android performs significant post-processing on the class files, and Android devices are not nearly as constrained as J2ME devices. Unless you're writing code to target both platforms, I'd change the lint configuration.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152