|
1 | | -<p>Generic types should not be used raw (without type parameters). To fix this issue, add the type parameters.</p> |
| 1 | +<p>Generic types should not be used raw (without type arguments). To fix this issue, add the type parameters.</p> |
2 | 2 | <h2>Why is this an issue?</h2> |
3 | 3 | <p>A generic type is a generic class or interface that is parameterized over types. For example, <code>java.util.List</code> has one type parameter: |
4 | 4 | the type of its elements.</p> |
5 | | -<p>When generic types are used raw (without type parameters), the compiler is not able to do generic type checking. For this reason, it is sometimes |
6 | | -necessary to cast objects and defer type-checking to runtime.</p> |
| 5 | +<p>Using generic types raw (without binding arguments to the type parameters) prevents compile-time type checking for expressions that use these type |
| 6 | +parameters. Explicit type casts are necessary for them, which do perform a runtime type check that may fail with a |
| 7 | +<code>ClassCastException</code>.</p> |
7 | 8 | <h3>What is the potential impact?</h3> |
8 | | -<p>When a cast fails, a <code>ClassCastException</code> is thrown and the program most likely crashes. Therefore, this issue might impact the |
9 | | -availability and reliability of your application.</p> |
| 9 | +<p>The compiler cannot assert that the program is inherently type safe. When a cast fails, a <code>ClassCastException</code> is thrown during runtime |
| 10 | +and the program most likely crashes. Therefore, this issue might impact the availability and reliability of your application.</p> |
| 11 | +<h3>Exceptions</h3> |
| 12 | +<p>The rule does not raise an issue for the simple <code>instanceof</code> operator, which checks against runtime types where type parameter |
| 13 | +information has been erased. Since it does not return a rawly typed instance but a boolean value, it does not prevent compile-time type checking.</p> |
| 14 | +<p>This, however, is not the case for the <code>cast</code> operator as well as the extended <code>instanceof</code> operator which are both not an |
| 15 | +exception from this rule. Since they operate on the erased runtime type as well, they must use wildcard type arguments when checked against a |
| 16 | +parameterized type (see the examples).</p> |
10 | 17 | <h2>How to fix it</h2> |
11 | | -<p>You should add type parameters. In the case of collections, the type parameter(s) should correspond to the type of elements that the list is |
12 | | -intended to store.</p> |
| 18 | +<p>For any usage of parameterized types, bind the type parameters with type arguments. For example, when a function returns a list of strings, the |
| 19 | +return type is <code>List<String></code>, where the type parameter <code>E</code> in interface <code>List<E></code> is bound with the |
| 20 | +argument <code>String</code>.</p> |
| 21 | +<p>If the concrete binding is unknown, you still should not use the type raw. Use a wildcard type argument instead, with optional lower or upper |
| 22 | +bound, such as in <code>List<?></code> for a list whose element type is unknown, or <code>List<? extends Number></code> for a list whose |
| 23 | +element type is <code>Number</code> or a subtype of it.</p> |
13 | 24 | <h3>Code examples</h3> |
14 | 25 | <h4>Noncompliant code example</h4> |
15 | 26 | <pre data-diff-id="1" data-diff-type="noncompliant"> |
| 27 | +// List is supposed to store integers only |
16 | 28 | List integers = new ArrayList<>(); |
17 | 29 |
|
18 | | -// It is possible to add a string to a list that is supposed to be integers only |
| 30 | +// Yet, we can add strings, because we did not give |
| 31 | +// this information to the compiler |
19 | 32 | integers.add("Hello World!"); |
20 | 33 |
|
21 | | -Integer a = (Integer) integers.get(0); // ClassCastException! |
| 34 | +// Type is checked during runtime and will throw a ClassCastException |
| 35 | +Integer a = (Integer) integers.get(0); |
22 | 36 | </pre> |
23 | 37 | <h4>Compliant solution</h4> |
24 | 38 | <pre data-diff-id="1" data-diff-type="compliant"> |
| 39 | +// List is supposed to store integers, and we let the compiler know |
25 | 40 | List<Integer> integers = new ArrayList<>(); |
26 | 41 |
|
27 | | -// The program does not compile anymore with this mistake: |
28 | | -// integers.add("Hello World!"); |
| 42 | +// Now we can add only integers. |
| 43 | +// Adding a string results in a compile time error. |
29 | 44 | integers.add(42); |
30 | 45 |
|
31 | | -Integer a = integers.get(0); // No need to cast anymore. |
| 46 | +// No cast required anymore, and no possible ClassCastException |
| 47 | +Integer a = integers.get(0); |
| 48 | +</pre> |
| 49 | +<h4>Noncompliant code example</h4> |
| 50 | +<pre data-diff-id="2" data-diff-type="noncompliant"> |
| 51 | +String getStringFromForcedList(Object object) { |
| 52 | + // Cast expression and instanceof can check runtime type only. |
| 53 | + // The solution is _not_ to skip the type argument in that case. |
| 54 | + return object instanceof List stringList ? (String) stringList.getFirst(): ""; |
| 55 | +} |
| 56 | +</pre> |
| 57 | +<h4>Compliant solution</h4> |
| 58 | +<pre data-diff-id="2" data-diff-type="compliant"> |
| 59 | +String getStringFromForcedList(Object object) { |
| 60 | + // The solution is to use a wildcard type argument in that case. |
| 61 | + return object instanceof List<?> stringList ? (String) stringList.getFirst(): ""; |
| 62 | +} |
| 63 | +</pre> |
| 64 | +<h4>Noncompliant code example</h4> |
| 65 | +<pre data-diff-id="3" data-diff-type="noncompliant"> |
| 66 | +String getStringFromForcedList(Object object) { |
| 67 | + return object instanceof List stringList ? (String) stringList.getFirst(): ""; |
| 68 | +} |
| 69 | + |
| 70 | +String returnString() { |
| 71 | + Object object = List.of("Hello"); |
| 72 | + return getStringFromForcedList(object); |
| 73 | +} |
| 74 | +</pre> |
| 75 | +<h4>Compliant solution</h4> |
| 76 | +<pre data-diff-id="3" data-diff-type="compliant"> |
| 77 | +Object getObjectFromForcedList(Object object) { |
| 78 | + // You may also choose not to make assumptions about type arguments you cannot infer. |
| 79 | + return object instanceof List<?> list ? list.getFirst(): ""; |
| 80 | +} |
| 81 | + |
| 82 | +String returnString(Object object) { |
| 83 | + // Instead, delegate the decision to use-site, which may have more information. |
| 84 | + Object object = List.of("Hello"); |
| 85 | + return (String) getObjectFromForcedList(object); |
| 86 | +} |
32 | 87 | </pre> |
33 | | -<h3>How does this work?</h3> |
34 | | -<p>In the noncompliant example, <code>List</code> is used as a raw type. Even though the list stores integers, the compiler will type its elements as |
35 | | -<code>Object</code>, To use an element of the list as an integer, it needs to be cast first. But elements are not garanteed to be integers. In this |
36 | | -case, a <code>String</code> is erroneously appended to the list, causing the cast to <code>Integer</code> to fail.</p> |
37 | | -<p>When the type parameter is specified, this bug is detected by the compiler during type-checking. The cast is also unncessary in this case.</p> |
38 | 88 | <h2>Resources</h2> |
39 | 89 | <h3>Documentation</h3> |
40 | 90 | <ul> |
|
0 commit comments