33import static org .assertj .core .api .Assertions .assertThat ;
44import static org .junit .jupiter .api .Assertions .assertThrows ;
55
6+ import java .lang .reflect .Type ;
67import java .util .List ;
8+ import java .util .concurrent .ConcurrentHashMap ;
9+ import java .util .concurrent .atomic .AtomicInteger ;
10+ import java .util .function .Function ;
711import org .junit .jupiter .api .Test ;
812
913class GenericTest {
@@ -23,21 +27,54 @@ void throwsIfNotParameterized() {
2327
2428 @ Test
2529 void resolvesMultipleGenericTypes () {
30+ class ConcurrentHashMapWithStats <K , V > extends ConcurrentHashMap <K , V > {
31+ final AtomicInteger misses = new AtomicInteger (0 );
32+
33+ @ Override
34+ public V computeIfAbsent (K key , Function <? super K , ? extends V > compute ) {
35+ Function <? super K , ? extends V > computeAndTrackMisses =
36+ (Function <K , V >)
37+ k -> {
38+ misses .incrementAndGet ();
39+ return compute .apply (k );
40+ };
41+ return super .computeIfAbsent (key , computeAndTrackMisses );
42+ }
43+ }
44+
45+ ConcurrentHashMapWithStats <Generic <?>, Type > cache = new ConcurrentHashMapWithStats <>();
46+ Generic .setCache (cache );
47+
2648 abstract class IKnowMyType <Foo , Bar > {
27- final Generic <Foo > foo = new Generic <Foo >(getClass ()) {};
28- final Generic <Bar > bar = new Generic <Bar >(getClass ()) {};
49+ final Generic <Foo > foo1 = new Generic <Foo >(getClass ()) {};
50+ final Generic <Foo > foo2 = new Generic <Foo >(getClass ()) {};
51+ final Generic <Bar > bar1 = new Generic <Bar >(getClass ()) {};
52+ final Generic <Bar > bar2 = new Generic <Bar >(getClass ()) {};
2953 }
3054
55+ // no reads upon construction -> no cache misses
3156 IKnowMyType <String , Integer > subj = new IKnowMyType <String , Integer >() {};
57+ assertThat (cache .misses ).hasValue (0 );
58+
59+ // reading the same generic instance twice -> 1 miss
60+ assertThat (subj .foo1 .resolve ()).isEqualTo (String .class );
61+ assertThat (subj .foo1 .resolve ()).isEqualTo (String .class );
62+ assertThat (cache .misses ).hasValue (1 );
3263
33- assertThat ( subj . foo . resolve ()). isEqualTo ( String . class );
34- assertThat (subj .bar .resolve ()).isEqualTo (Integer .class );
35- assertThat (subj .bar .resolve ()).isEqualTo (Integer .class );
36- assertThat (subj . bar . resolve ()). isEqualTo ( Integer . class );
64+ // reading the same generic, but difference instance twice -> still 1 miss
65+ assertThat (subj .foo2 .resolve ()).isEqualTo (String .class );
66+ assertThat (subj .foo2 .resolve ()).isEqualTo (String .class );
67+ assertThat (cache . misses ). hasValue ( 1 );
3768
38- // caching test
39- assertThat (subj .foo .numberOfScans ).hasValue (1 );
40- assertThat (subj .bar .numberOfScans ).hasValue (1 );
69+ // reading different generic (same instance, twice) -> +1 new miss => 2 total
70+ assertThat (subj .bar1 .resolve ()).isEqualTo (Integer .class );
71+ assertThat (subj .bar1 .resolve ()).isEqualTo (Integer .class );
72+ assertThat (cache .misses ).hasValue (2 );
73+
74+ // reading the same generic, difference instance -> no new misses => 2 total
75+ assertThat (subj .bar2 .resolve ()).isEqualTo (Integer .class );
76+ assertThat (subj .bar2 .resolve ()).isEqualTo (Integer .class );
77+ assertThat (cache .misses ).hasValue (2 );
4178 }
4279
4380 @ Test
@@ -48,4 +85,10 @@ abstract class IKnowMyType<T> {
4885 IKnowMyType <List <String >> subj = new IKnowMyType <List <String >>() {};
4986 assertThat (subj .foo .resolve ()).isEqualTo (List .class );
5087 }
88+
89+ @ Test
90+ void resolvesRawTypes () {
91+ Generic <String > foo = new Generic <String >(getClass ()) {};
92+ assertThat (foo .resolve ()).isEqualTo (String .class );
93+ }
5194}
0 commit comments