Why prefer non-static inner classes over static ones?

This question is about whether to make a nested class in Java to be a static nested class or an inner nested class. I searched around here and on Stack Overflow, but couldn’t really find any questions regarding the design implications of this decision.

The questions I found are asking about the difference between static and inner nested classes, which is clear to me. However, I have not yet found a convincing reason to ever use a static nested class in Java — with the exception of anonymous classes, which I do not consider for this question.

Here’s my understanding of the effect of using static nested classes:

  • Less coupling: We generally get less coupling, as the class cannot directly access its outer class’s attributes. Less coupling generally means better code quality, easier testing, refactoring, etc.
  • Single Class: The class loader need not take care of a new class each time, we create an object of the outer class. We just get new objects for the same class over and over.

For an inner class, I generally find that people consider access to the outer class’s attributes as a pro. I beg to differ in this regard from a design point of view, as this direct access means we have a high coupling and if we ever want to extract the nested class into its separate top-level class, we can only do so after essentially turning it into a static nested class.

So my question comes down to this: Am I wrong in assuming that the attribute access available to non-static inner classes leads to high coupling, hence to lower code quality, and that I infer from this that (non-anonymous) nested classes should generally be static?

Or in other words: Is there a convincing reason why one would prefer a nested inner class?

4

Joshua Bloch in Item 22 of his book “Effective Java Second Edition” tells when to use which kind of nested class and why. There are some quotes below:

One common use of a static member class is as a public helper class, useful only in conjunction with its outer class. For example, consider an enum describing the operations supported by a calculator. The Operation enum should be a public static member class of the Calculator class. Clients of Calculator could then refer to operations using names like Calculator.Operation.PLUS and Calculator.Operation.MINUS.

One common use of a nonstatic member class is to define an Adapter that allows an instance of the outer class to be viewed as an instance of some unrelated class. For example, implementations of the Map interface typically use nonstatic member classes to implement their collection views, which are returned by Map’s keySet, entrySet, and values methods. Similarly, implementations of the collection interfaces, such as Set and List, typically use nonstatic member classes to implement their iterators:

// Typical use of a nonstatic member class
public class MySet<E> extends AbstractSet<E> {
    ... // Bulk of the class omitted

    public Iterator<E> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<E> {
        ...
    }
}

If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration, making it a static rather than a nonstatic member class.

4

You are correct in assuming that the attribute access available to non-static inner classes leads to high coupling, hence to lower code quality, and (non-anonymous and non-local) inner classes should generally be static.

Design implications of decision to make inner class non-static are laid out in Java Puzzlers, Puzzle 90 (bold font in below quote is mine):

Whenever you write a member class, ask yourself, Does this class really need an enclosing instance? If the answer is no, make it static. Inner classes are sometimes useful, but they can easily introduce complications that make a program difficult to understand. They have complex interactions with generics (Puzzle 89), reflection (Puzzle 80), and inheritance (this puzzle). If you declare Inner1 to be static, the problem goes away. If you also declare Inner2 to be static, you can actually understand what the program does: a nice bonus indeed.

In summary, it is rarely appropriate for one class to be both an inner class and a subclass of another. More generally, it is rarely appropriate to extend an inner class; if you must, think long and hard about the enclosing instance. Also, prefer static nested classes to non-static. Most member classes can and should be declared static.

If you’re interested, a more detailed analysis of Puzzle 90 is provided in this answer at Stack Overflow.


It is worth noting that above is essentially an extended version of the guidance given in Java Classes and Objects tutorial:

Use a non-static nested class (or inner class) if you require access to an enclosing instance’s non-public fields and methods. Use a static nested class if you don’t require this access.

So, the answer to the question you asked in other words per tutorial is, the only convincing reason to use non-static is when access to an enclosing instance’s non-public fields and methods is required.

Tutorial wording is somewhat broad (this may be the reason why Java Puzzlers make an attempt to strengthen it and narrow it down). In particular, directly accessing enclosing instance fields has never been really required in my experience – in the sense that alternative ways like passing these as constructor / method parameters always turned easier to debug and maintain.


Overall, my (quite painful) encounters with debugging inner classes directly accessing fields of enclosing instance made strong impression that this practice resembles use of global state, along with known evils associated with it.

Of course, Java makes it so that damage of such a “quasi global” is contained within enclosing class, but when I had to debug particular inner class, it felt like such a band aid didn’t help to reduce pain: I still had to keep in mind “foreign” semantic and details instead of fully focusing on analysis of a particular troublesome object.


For the sake of completeness, there may be cases where above reasoning doesn’t apply. For example, per my reading of map.keySet javadocs, this feature suggests tight coupling and as a result, invalidates arguments against non-static classes:

Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa…

Not that above would somehow make involved code easier to maintain, test and debug mind you, but it at least could allow one to argue that complication matches / is justified by intended functionality.

4

It can be used to create a factory pattern. A factory pattern is often used when the created objects need additional constructor parameters to function, but are tedious to provide each time:

Consider:

public class QueryFactory {
    @Inject private Database database;

    public Query create(String sql) {
        return new Query(database, sql);
    }
}

public class Query {
    private final Database database;
    private final String sql;

    public Query(Database database, String sql) {
        this.database = database;
        this.sql = sql;
    } 

    public List performQuery() {
        return database.query(sql);
    }
}

Used as:

Query q = queryFactory.create("SELECT * FROM employees");

q.performQuery();

The same thing could be achieved with a non-static inner class:

public class QueryFactory {
    @Inject private Database database;

    public class Query {
        private final String sql;

        public Query(String sql) {
            this.sql = sql;
        }

        public List performQuery() {
            return database.query(sql);
        }
    }
}

Use as:

Query q = queryFactory.new Query("SELECT * FROM employees");

q.performQuery();

Factories benefit from having specifically named methods, like create, createFromSQL or createFromTemplate. The same can be done here as well in the form of multiple inner classes:

public class QueryFactory {

    public class SQL { ... }
    public class Native { ... }
    public class Builder { ... }

}

Less parameter passing is needed from factory to constructed class, but readability may suffer a bit.

Compare:

Queries.Native q = queries.new Native("select * from employees");

vs

Query q = queryFactory.createNative("select * from employees");

Some misconceptions:

Less coupling: We generally get less coupling, as the [static inner] class cannot
directly access its outer class’s attributes.

IIRC – Actually, it can. (Of course if they are object fields, it will not have an outer object to look at. Nonetheless, if it does get such an object from anywhere, then it can directly access those fields).

Single Class: The class loader need not take care of a new class each
time, we create an object of the outer class. We just get new objects
for the same class over and over.

I’m not quite sure what you mean here. The class loader is only involved twice in total, once for each class (when the class is loaded).


Rambling follows:

In Javaland we seem to be a bit scared to create classes. I think it has to feel like a significant concept. It generally belongs in its own file. It needs significant boilerplate. It needs attention to its design.

Versus other languages – eg Scala, or maybe C++: There is absolutely no drama creating a tiny holder (class, struct, whatever) anytime that two bits of data belong together. Flexible access rules help you by cutting down the boilerplate, letting you keep the related code physically closer. This reduces your ‘mind-distance’ between the two classes.

We can wave away design concerns over the direct access, by keeping the inner class private.

(…If you’d asked ‘why a non-static public inner class?’ that’s a very good question – I don’t think I’ve ever yet found a justified case for those).

You can use them anytime it helps with code readability and avoiding DRY violations and boilerplate. I don’t have a ready example because it doesn’t happen all that often in my experience, but it does happen.


Static inner classes is still a good default approach, btw. Non-static has an extra hidden field generated through which the inner class refers to the outer.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật