rspec/rules/S2694/java/rule.adoc

151 lines
4.1 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2021-04-28 16:49:39 +02:00
A non-static inner class has a reference to its outer class, and access to the outer class' fields and methods. That class reference makes the inner class larger and could cause the outer class instance to live in memory longer than necessary.
If the reference to the outer class isn't used, it is more efficient to make the inner class ``++static++`` (also called nested). If the reference is used only in the class constructor, then explicitly pass a class reference to the constructor. If the inner class is anonymous, it will also be necessary to name it.
However, while a nested/``++static++`` class would be more efficient, it's worth noting that there are semantic differences between an inner class and a nested one:
* an inner class can only be instantiated within the context of an instance of the outer class.
* a nested (``++static++``) class can be instantiated independently of the outer class.
== How to fix it
There are two scenarios in which this rule will raise an issue:
2021-04-28 16:49:39 +02:00
1. On an _inner class_: make it `static`.
2. On a _local class_: extract it as a `static` _inner class_.
=== Code examples
==== Noncompliant code example
Inner classes that don't use the outer class reference should be static.
[source,java,diff-id=1,diff-type=noncompliant]
2021-04-28 16:49:39 +02:00
----
public class Fruit {
// ...
public class Seed { // Noncompliant; there's no use of the outer class reference so make it static
int germinationDays = 0;
public Seed(int germinationDays) {
this.germinationDays = germinationDays;
}
public int getGerminationDays() {
return germinationDays;
}
}
}
----
==== Compliant solution
[source,java,diff-id=1,diff-type=compliant]
2021-04-28 16:49:39 +02:00
----
public class Fruit {
// ...
public static class Seed {
int germinationDays = 0;
public Seed(int germinationDays) {
this.germinationDays = germinationDays;
}
public int getGerminationDays() {
return germinationDays;
}
}
}
----
Local classes that don't use the outer class reference should be extracted as a static inner classes.
==== Noncompliant code example
[source,java,diff-id=2,diff-type=noncompliant]
----
public class Foo {
public Foo() {
class Bar { // Noncompliant
void doSomething() {
// ...
}
}
new Bar().doSomething();
}
public void method() {
class Baz { // Noncompliant
void doSomething() {
// ...
}
}
new Baz().doSomething();
}
}
----
==== Compliant solution
[source,java,diff-id=2,diff-type=compliant]
----
public class Foo {
public Foo() {
new Bar().doSomething();
}
public void method() {
new Baz().doSomething();
}
private static class Bar { // Compliant
void doSomething() {
// ...
}
}
private static class Baz { // Compliant
void doSomething() {
// ...
}
}
}
----
== Resources
=== Documentation
* https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html[Oracle Java SE - Nested Classes]
* https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html[Oracle Java SE - Local Classes]
=== Articles & blog posts
* https://www.geeksforgeeks.org/difference-between-static-and-non-static-nested-class-in-java/[GeeksforGeeks - Difference between static and non-static nested class in Java]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Make this a [named] "static" inner class.
'''
== Comments And Links
(visible only on this page)
=== on 6 Oct 2015, 19:33:30 Ann Campbell wrote:
\[~nicolas.peru], _http://www.securingjava.com/chapter-seven/chapter-seven-1.html[Securing Java]_ (see Rule 5) says that inner classes (presumably only non-``++static++``) are security holes because the compiler translates them to ordinary classes with ``++package++`` accessibility, and "upgrades" the owning's class's ``++private++`` member visibility to ``++package++``.
The upshot is a recommendation against using inner classes. Since those problems go away if the inner class is ``++static++``, I'm wondering whether to combine "Don't use non-static inner classes" with this rule or handle it in a separate RSpec. WDYT?
endif::env-github,rspecator-view[]