-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Modify rule S6320: Adapt to LaYC format (#3158)
- Loading branch information
Showing
1 changed file
with
109 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,136 @@ | ||
This issue indicates that a cast operation will fail and throw a `ClassCastException`. | ||
To fix it, make sure only references to objects with a compatible type can be cast. | ||
|
||
== Why is this an issue? | ||
|
||
A cast operation allows an object to be "converted" from one type to another. | ||
The compiler raises an error if it can determine that the target type is incompatible with the declared type of the object, otherwise it accepts the cast. | ||
However, depending on the actual runtime type of the object, a cast operation may fail at runtime. | ||
When a cast operation fails, a `ClassCastException` is thrown. | ||
|
||
=== What is the potential impact? | ||
|
||
This type of exception is usually unexpected. | ||
It causes the program to crash or puts it into an inconsistent state. | ||
Therefore, this issue might impact the availability and reliability of your application, or even result in data loss. | ||
|
||
== How to fix it | ||
|
||
When an object is cast, the code makes assumptions about the type of the object. | ||
A `ClassCastException` indicates that this assumption has been broken. | ||
If the assumption is reasonable, then some prior logic should ensure that only objects of a compatible type can be cast. | ||
You should try to identify the code responsible for these checks and fix it. | ||
|
||
=== Code examples | ||
|
||
==== Noncompliant code example | ||
|
||
[source,java,diff-id=1,diff-type=noncompliant] | ||
---- | ||
String hexString(Object o) { | ||
return Integer.toHexString((Integer) o); // Noncompliant if hexString is called with a String for example | ||
} | ||
---- | ||
|
||
==== Compliant solution | ||
|
||
A cast operation makes it possible to convert an object from one type to another one. The compiler raises an error when it can determine that the target type is incompatible with the declared type of the object. Other cases are accepted by the compiler. However, depending on the actual runtime type of the object, a cast operation may fail at runtime with a `ClassCastException` which can crash the program. This kind of failure happens when a piece of code makes an invalid assumption about the runtime type of an object. | ||
One possible solution is to change `hexString` to only accept integers and adapt call sites. | ||
|
||
The appropriate fix for those cast operations highly depends on the context: adding a condition before the cast or changing the runtime type of the object are some of the possible solutions. | ||
[source,java,diff-id=1,diff-type=compliant] | ||
---- | ||
String hexString(Integer i) { | ||
return Integer.toHexString(i); | ||
} | ||
---- | ||
|
||
=== Noncompliant code example | ||
Another one is to return a default value for types that are not `Integer`. | ||
Here, the `if` statement with the condition relying on `instanceof` prevents references to objects with an incompatible type from making it to the cast operation. | ||
|
||
[source,java] | ||
[source,java,diff-id=1,diff-type=compliant] | ||
---- | ||
List<String> list = new LinkedList<>(); | ||
if (someCondition) { | ||
list = new ArrayList<>(); | ||
String hexString(Object o) { | ||
if (o instanceof Integer) { | ||
return Integer.toHexString((Integer) o); | ||
} | ||
return "0x0"; | ||
} | ||
((LinkedList<String>) list).addLast("abc"); // Noncompliant, throws a ClassCastException when someCondition is true | ||
---- | ||
|
||
=== Compliant solution | ||
The solution to adopt depends on which part of the code should be responsible to handle non-Integer types. | ||
|
||
=== Going the extra mile | ||
|
||
[source,java] | ||
Casting is considered an anti-pattern in object-oriented programming. | ||
It is sometimes necessary, but there often is a better alternative. | ||
Consider: | ||
|
||
* Polymorphism | ||
* The visitor design pattern | ||
* Pattern matching | ||
|
||
They come with some guarantees from the compiler, making your code more reliable. | ||
|
||
==== Noncompliant code example | ||
|
||
[source,java,diff-id=2,diff-type=noncompliant] | ||
---- | ||
List<String> list = new LinkedList<>(); | ||
if (someCondition) { | ||
list = new ArrayList<>(); | ||
int foo(Shape shape) { | ||
if (shape instanceof Circle) { | ||
Circle circle = (Circle) shape; | ||
// Code for objects of type Circle | ||
} else if (shape instanceof Square) { | ||
Square square = (Square) shape; | ||
// Code for objects of type Square | ||
} | ||
// Default | ||
} | ||
if (list instanceof LinkedList) { | ||
((LinkedList<String>) list).addLast("abc"); | ||
---- | ||
|
||
==== Compliant solution | ||
|
||
[source,java,diff-id=2,diff-type=compliant] | ||
---- | ||
int foo(Shape shape) { | ||
return shape.fooValue(...); // Code was moved into subclasses | ||
} | ||
---- | ||
|
||
or | ||
|
||
[source,java] | ||
[source,java,diff-id=2,diff-type=compliant] | ||
---- | ||
List<String> list = new LinkedList<>(); | ||
if (someCondition) { | ||
list = new ArrayList<>(); | ||
int foo(Shape shape) { | ||
return shape.accept(new FooVisitor()); // Code was moved into FooVisitor | ||
} | ||
list.add("abc"); | ||
---- | ||
|
||
or | ||
|
||
[source,java] | ||
[source,java,diff-id=2,diff-type=compliant] | ||
---- | ||
LinkedList<String> list = new LinkedList<>(); | ||
list.addLast("abc"); | ||
// Java 14+ required | ||
int foo(Shape shape) { | ||
if (shape instanceof Circle c) { | ||
// Code for objects of type Circle | ||
} else if (shape instanceof Square s) { | ||
// Code for objects of type Square | ||
} | ||
} | ||
---- | ||
|
||
== Resources | ||
|
||
=== Documentation | ||
|
||
* https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ClassCastException.html[ClassCastException] | ||
|
||
=== Articles & blog posts | ||
|
||
* https://refactoring.guru/design-patterns/visitor[Refactoring Guru: Visitor] | ||
* https://openjdk.org/jeps/394[JEP 394: Pattern Matching for `instanceof`] | ||
|
||
=== Standards | ||
|
||
* https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.16[JLS: Cast Expressions] | ||
* https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2[JLS: Type Comparison Operator `instanceof`] | ||
|