Extend ClassKind with generic type that inherits from DexClass

Change-Id: I2046182482098f6aca02522dd25b4447d9a28126
diff --git a/src/main/java/com/android/tools/r8/JarDiff.java b/src/main/java/com/android/tools/r8/JarDiff.java
index 817b461..a268496 100644
--- a/src/main/java/com/android/tools/r8/JarDiff.java
+++ b/src/main/java/com/android/tools/r8/JarDiff.java
@@ -5,15 +5,14 @@
 
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.graph.ClassKind;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.JarApplicationReader;
 import com.android.tools.r8.graph.JarClassFileReader;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StreamUtils;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -21,7 +20,6 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -136,26 +134,11 @@
         inputJar.getProgramResource(descriptor).getByteStream());
   }
 
-  private DexProgramClass getDexProgramClass(Path path, byte[] bytes) throws IOException {
-
-    class Collector implements Consumer<DexClass> {
-
-      private DexClass dexClass;
-
-      @Override
-      public void accept(DexClass dexClass) {
-        this.dexClass = dexClass;
-      }
-
-      public DexClass get() {
-        assert dexClass != null;
-        return dexClass;
-      }
-    }
-
-    Collector collector = new Collector();
-    JarClassFileReader reader = new JarClassFileReader(applicationReader, collector);
-    reader.read(new PathOrigin(path), ClassKind.PROGRAM, bytes);
+  private DexProgramClass getDexProgramClass(Path path, byte[] bytes) {
+    Box<DexProgramClass> collector = new Box<>();
+    JarClassFileReader<DexProgramClass> reader =
+        new JarClassFileReader<>(applicationReader, collector::set, ClassKind.PROGRAM);
+    reader.read(new PathOrigin(path), bytes);
     return collector.get().asProgramClass();
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index b46cd9b..4b035d4 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -151,7 +151,7 @@
       // TODO: try and preload less classes.
       readProguardMap(proguardMap, builder, executorService, futures);
       ClassReader classReader = new ClassReader(executorService, futures);
-      JarClassFileReader jcf = classReader.readSources();
+      JarClassFileReader<DexProgramClass> jcf = classReader.readSources();
       ThreadUtils.awaitFutures(futures);
       classReader.initializeLazyClassCollection(builder);
       for (ProgramResourceProvider provider : inputApp.getProgramResourceProviders()) {
@@ -296,46 +296,46 @@
       this.futures = futures;
     }
 
-    private <T extends DexClass> void readDexSources(
-        List<ProgramResource> dexSources, ClassKind classKind, Queue<T> classes)
+    private void readDexSources(List<ProgramResource> dexSources, Queue<DexProgramClass> classes)
         throws IOException, ResourceException {
       if (dexSources.size() > 0) {
-        List<DexParser> dexParsers = new ArrayList<>(dexSources.size());
+        List<DexParser<DexProgramClass>> dexParsers = new ArrayList<>(dexSources.size());
         int computedMinApiLevel = options.minApiLevel;
         for (ProgramResource input : dexSources) {
           DexReader dexReader = new DexReader(input);
           if (options.passthroughDexCode) {
             computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
           }
-          dexParsers.add(new DexParser(dexReader, classKind, options));
+          dexParsers.add(new DexParser<>(dexReader, PROGRAM, options));
         }
 
         options.minApiLevel = computedMinApiLevel;
-        for (DexParser dexParser : dexParsers) {
+        for (DexParser<DexProgramClass> dexParser : dexParsers) {
           dexParser.populateIndexTables();
         }
         // Read the DexCode items and DexProgramClass items in parallel.
         if (!options.skipReadingDexCode) {
-          for (DexParser dexParser : dexParsers) {
-            futures.add(executorService.submit(() -> {
-              dexParser.addClassDefsTo(
-                  classKind.bridgeConsumer(classes::add)); // Depends on Methods, Code items etc.
-            }));
+          for (DexParser<DexProgramClass> dexParser : dexParsers) {
+            futures.add(
+                executorService.submit(
+                    () -> {
+                      dexParser.addClassDefsTo(classes::add); // Depends on Methods, Code items etc.
+                    }));
           }
         }
       }
     }
 
-    private <T extends DexClass> JarClassFileReader readClassSources(
-        List<ProgramResource> classSources, ClassKind classKind, Queue<T> classes) {
-      JarClassFileReader reader = new JarClassFileReader(
-          application, classKind.bridgeConsumer(classes::add));
+    private JarClassFileReader<DexProgramClass> readClassSources(
+        List<ProgramResource> classSources, Queue<DexProgramClass> classes) {
+      JarClassFileReader<DexProgramClass> reader =
+          new JarClassFileReader<>(application, classes::add, PROGRAM);
       // Read classes in parallel.
       for (ProgramResource input : classSources) {
         futures.add(
             executorService.submit(
                 () -> {
-                  reader.read(input, classKind);
+                  reader.read(input);
                   // No other way to have a void callable, but we want the IOException from read
                   // to be wrapped into an ExecutionException.
                   return null;
@@ -344,7 +344,7 @@
       return reader;
     }
 
-    JarClassFileReader readSources() throws IOException, ResourceException {
+    JarClassFileReader<DexProgramClass> readSources() throws IOException, ResourceException {
       Collection<ProgramResource> resources = inputApp.computeAllProgramResources();
       List<ProgramResource> dexResources = new ArrayList<>(resources.size());
       List<ProgramResource> cfResources = new ArrayList<>(resources.size());
@@ -356,12 +356,14 @@
           cfResources.add(resource);
         }
       }
-      readDexSources(dexResources, PROGRAM, programClasses);
-      return readClassSources(cfResources, PROGRAM, programClasses);
+      readDexSources(dexResources, programClasses);
+      return readClassSources(cfResources, programClasses);
     }
 
-    private <T extends DexClass> ClassProvider<T> buildClassProvider(ClassKind classKind,
-        Queue<T> preloadedClasses, List<ClassFileResourceProvider> resourceProviders,
+    private <T extends DexClass> ClassProvider<T> buildClassProvider(
+        ClassKind<T> classKind,
+        Queue<T> preloadedClasses,
+        List<ClassFileResourceProvider> resourceProviders,
         JarApplicationReader reader) {
       List<ClassProvider<T>> providers = new ArrayList<>();
 
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 2408b27..cd78995 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -82,14 +82,14 @@
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
-public class DexParser {
+public class DexParser<T extends DexClass> {
 
   private final int NO_INDEX = -1;
   private final Origin origin;
   private DexReader dexReader;
   private final DexSection[] dexSections;
   private int[] stringIDs;
-  private final ClassKind classKind;
+  private final ClassKind<T> classKind;
   private final InternalOptions options;
   private Object2LongMap<String> checksums;
 
@@ -102,7 +102,8 @@
   }
 
   private static DexSection[] parseMapFrom(DexReader dexReader) {
-    DexParser dexParser = new DexParser(dexReader, ClassKind.PROGRAM, new InternalOptions());
+    DexParser<DexProgramClass> dexParser =
+        new DexParser<>(dexReader, ClassKind.PROGRAM, new InternalOptions());
     return dexParser.dexSections;
   }
 
@@ -127,7 +128,7 @@
   // Factory to canonicalize certain dexitems.
   private final DexItemFactory dexItemFactory;
 
-  public DexParser(DexReader dexReader, ClassKind classKind, InternalOptions options) {
+  public DexParser(DexReader dexReader, ClassKind<T> classKind, InternalOptions options) {
     assert dexReader.getOrigin() != null;
     this.origin = dexReader.getOrigin();
     this.dexReader = dexReader;
@@ -419,14 +420,14 @@
     return result;
   }
 
-  private <T> Object cacheAt(int offset, Supplier<T> function, Supplier<T> defaultValue) {
+  private <S> Object cacheAt(int offset, Supplier<S> function, Supplier<S> defaultValue) {
     if (offset == 0) {
       return defaultValue.get();
     }
     return cacheAt(offset, function);
   }
 
-  private <T> Object cacheAt(int offset, Supplier<T> function) {
+  private <S> Object cacheAt(int offset, Supplier<S> function) {
     if (offset == 0) {
       return null;  // return null for offset zero.
     }
@@ -704,7 +705,7 @@
     return methods;
   }
 
-  void addClassDefsTo(Consumer<DexClass> classCollection) {
+  void addClassDefsTo(Consumer<T> classCollection) {
     final DexSection dexSection = lookupSection(Constants.TYPE_CLASS_DEF_ITEM);
     final int length = dexSection.length;
     indexedItems.initializeClasses(length);
@@ -796,7 +797,7 @@
       ChecksumSupplier checksumSupplier =
           finalChecksum == null ? DexProgramClass::invalidChecksumRequest : c -> finalChecksum;
 
-      DexClass clazz =
+      T clazz =
           classKind.create(
               type,
               Kind.DEX,
@@ -925,7 +926,7 @@
           int hsize = dexReader.getSleb128();
           int realHsize = Math.abs(hsize);
           // - handlers	encoded_type_addr_pair[abs(size)]
-          TryHandler.TypeAddrPair pairs[] = new TryHandler.TypeAddrPair[realHsize];
+          TryHandler.TypeAddrPair[] pairs = new TryHandler.TypeAddrPair[realHsize];
           for (int j = 0; j < realHsize; j++) {
             int typeIdx = dexReader.getUleb128();
             int addr = dexReader.getUleb128();
@@ -1415,7 +1416,7 @@
           List<DexType> members = DexAnnotation.getMemberClassesFromAnnotation(annotation, factory);
           if (memberClasses == null) {
             memberClasses = members;
-          } else {
+          } else if (members != null) {
             memberClasses.addAll(members);
           }
         } else if (DexAnnotation.isSignatureAnnotation(annotation, factory)
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 3929cfb..baf5365 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -9,97 +9,97 @@
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.origin.Origin;
 import java.util.List;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /** Kind of the application class. Can be program, classpath or library. */
-public enum ClassKind {
-  PROGRAM(DexProgramClass::new, DexClass::isProgramClass),
-  CLASSPATH(
-      (type,
-          kind,
-          origin,
-          accessFlags,
-          superType,
-          interfaces,
-          sourceFile,
-          nestHost,
-          nestMembers,
-          enclosingMember,
-          innerClasses,
-          classSignature,
-          annotations,
-          staticFields,
-          instanceFields,
-          directMethods,
-          virtualMethods,
-          skipNameValidationForTesting,
-          checksumSupplier) -> {
-        return new DexClasspathClass(
-            type,
-            kind,
-            origin,
-            accessFlags,
-            superType,
-            interfaces,
-            sourceFile,
-            nestHost,
-            nestMembers,
-            enclosingMember,
-            innerClasses,
-            classSignature,
-            annotations,
-            staticFields,
-            instanceFields,
-            directMethods,
-            virtualMethods,
-            skipNameValidationForTesting);
-      },
-      DexClass::isClasspathClass),
-  LIBRARY(
-      (type,
-          kind,
-          origin,
-          accessFlags,
-          superType,
-          interfaces,
-          sourceFile,
-          nestHost,
-          nestMembers,
-          enclosingMember,
-          innerClasses,
-          classSignature,
-          annotations,
-          staticFields,
-          instanceFields,
-          directMethods,
-          virtualMethods,
-          skipNameValidationForTesting,
-          checksumSupplier) -> {
-        return new DexLibraryClass(
-            type,
-            kind,
-            origin,
-            accessFlags,
-            superType,
-            interfaces,
-            sourceFile,
-            nestHost,
-            nestMembers,
-            enclosingMember,
-            innerClasses,
-            classSignature,
-            annotations,
-            staticFields,
-            instanceFields,
-            directMethods,
-            virtualMethods,
-            skipNameValidationForTesting);
-      },
-      DexClass::isLibraryClass);
+public class ClassKind<C extends DexClass> {
+  public static ClassKind<DexProgramClass> PROGRAM =
+      new ClassKind<>(DexProgramClass::new, DexClass::isProgramClass);
+  public static ClassKind<DexClasspathClass> CLASSPATH =
+      new ClassKind<>(
+          (type,
+              kind,
+              origin,
+              accessFlags,
+              superType,
+              interfaces,
+              sourceFile,
+              nestHost,
+              nestMembers,
+              enclosingMember,
+              innerClasses,
+              classSignature,
+              annotations,
+              staticFields,
+              instanceFields,
+              directMethods,
+              virtualMethods,
+              skipNameValidationForTesting,
+              checksumSupplier) ->
+              new DexClasspathClass(
+                  type,
+                  kind,
+                  origin,
+                  accessFlags,
+                  superType,
+                  interfaces,
+                  sourceFile,
+                  nestHost,
+                  nestMembers,
+                  enclosingMember,
+                  innerClasses,
+                  classSignature,
+                  annotations,
+                  staticFields,
+                  instanceFields,
+                  directMethods,
+                  virtualMethods,
+                  skipNameValidationForTesting),
+          DexClass::isClasspathClass);
+  public static final ClassKind<DexLibraryClass> LIBRARY =
+      new ClassKind<>(
+          (type,
+              kind,
+              origin,
+              accessFlags,
+              superType,
+              interfaces,
+              sourceFile,
+              nestHost,
+              nestMembers,
+              enclosingMember,
+              innerClasses,
+              classSignature,
+              annotations,
+              staticFields,
+              instanceFields,
+              directMethods,
+              virtualMethods,
+              skipNameValidationForTesting,
+              checksumSupplier) ->
+              new DexLibraryClass(
+                  type,
+                  kind,
+                  origin,
+                  accessFlags,
+                  superType,
+                  interfaces,
+                  sourceFile,
+                  nestHost,
+                  nestMembers,
+                  enclosingMember,
+                  innerClasses,
+                  classSignature,
+                  annotations,
+                  staticFields,
+                  instanceFields,
+                  directMethods,
+                  virtualMethods,
+                  skipNameValidationForTesting),
+          DexClass::isLibraryClass);
 
-  private interface Factory {
-    DexClass create(
+  private interface Factory<C extends DexClass> {
+    C create(
         DexType type,
         Kind kind,
         Origin origin,
@@ -121,15 +121,15 @@
         ChecksumSupplier checksumSupplier);
   }
 
-  private final Factory factory;
+  private final Factory<C> factory;
   private final Predicate<DexClass> check;
 
-  ClassKind(Factory factory, Predicate<DexClass> check) {
+  ClassKind(Factory<C> factory, Predicate<DexClass> check) {
     this.factory = factory;
     this.check = check;
   }
 
-  public DexClass create(
+  public C create(
       DexType type,
       Kind kind,
       Origin origin,
@@ -174,12 +174,4 @@
   public boolean isOfKind(DexClass clazz) {
     return check.test(clazz);
   }
-
-  public <T extends DexClass> Consumer<DexClass> bridgeConsumer(Consumer<T> consumer) {
-    return clazz -> {
-      assert isOfKind(clazz);
-      @SuppressWarnings("unchecked") T specialized = (T) clazz;
-      consumer.accept(specialized);
-    };
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index fabdde7..88596f9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -66,10 +66,8 @@
 import org.objectweb.asm.Type;
 import org.objectweb.asm.TypePath;
 
-/**
- * Java/Jar class reader for constructing dex/graph structure.
- */
-public class JarClassFileReader {
+/** Java/Jar class reader for constructing dex/graph structure. */
+public class JarClassFileReader<T extends DexClass> {
 
   private static final byte[] CLASSFILE_HEADER = ByteBuffer.allocate(4).putInt(0xCAFEBABE).array();
 
@@ -77,24 +75,25 @@
   private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
 
   private final JarApplicationReader application;
-  private final Consumer<DexClass> classConsumer;
+  private final Consumer<T> classConsumer;
+  private final ClassKind<T> classKind;
 
   public JarClassFileReader(
-      JarApplicationReader application, Consumer<DexClass> classConsumer) {
+      JarApplicationReader application, Consumer<T> classConsumer, ClassKind<T> classKind) {
     this.application = application;
     this.classConsumer = classConsumer;
+    this.classKind = classKind;
   }
 
-  public void read(ProgramResource resource, ClassKind classKind) throws ResourceException {
-    read(resource.getOrigin(), classKind, resource.getBytes());
+  public void read(ProgramResource resource) throws ResourceException {
+    read(resource.getOrigin(), resource.getBytes());
   }
 
-  public void read(Origin origin, ClassKind classKind, byte[] bytes) {
-    ExceptionUtils.withOriginAttachmentHandler(
-        origin, () -> internalRead(origin, classKind, bytes));
+  public void read(Origin origin, byte[] bytes) {
+    ExceptionUtils.withOriginAttachmentHandler(origin, () -> internalRead(origin, bytes));
   }
 
-  public void internalRead(Origin origin, ClassKind classKind, byte[] bytes) {
+  public void internalRead(Origin origin, byte[] bytes) {
     if (bytes.length < CLASSFILE_HEADER.length) {
       throw new CompilationError("Invalid empty classfile", origin);
     }
@@ -118,7 +117,7 @@
       }
     }
     reader.accept(
-        new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
+        new CreateDexClassVisitor<>(origin, classKind, reader.b, application, classConsumer),
         parsingOptions);
 
     // Read marker.
@@ -188,12 +187,12 @@
     return new DexEncodedAnnotation(application.getTypeFromDescriptor(desc), elements);
   }
 
-  private static class CreateDexClassVisitor extends ClassVisitor {
+  private static class CreateDexClassVisitor<T extends DexClass> extends ClassVisitor {
 
     private final Origin origin;
-    private final ClassKind classKind;
+    private final ClassKind<T> classKind;
     private final JarApplicationReader application;
-    private final Consumer<DexClass> classConsumer;
+    private final Consumer<T> classConsumer;
     private final ReparseContext context = new ReparseContext();
 
     // DexClass data.
@@ -221,10 +220,10 @@
 
     public CreateDexClassVisitor(
         Origin origin,
-        ClassKind classKind,
+        ClassKind<T> classKind,
         byte[] classCache,
         JarApplicationReader application,
-        Consumer<DexClass> classConsumer) {
+        Consumer<T> classConsumer) {
       super(ASM_VERSION);
       this.origin = origin;
       this.classKind = classKind;
@@ -350,7 +349,7 @@
                 accessFlags,
                 name,
                 version,
-                "must extend class java.lang.Object. Found: " + Objects.toString(superName)),
+                "must extend class java.lang.Object. Found: " + superName),
             origin);
       }
       checkName(name);
@@ -420,7 +419,7 @@
             type, defaultAnnotations, application.getFactory()));
       }
       checkReachabilitySensitivity();
-      DexClass clazz =
+      T clazz =
           classKind.create(
               type,
               Kind.CF,
@@ -483,7 +482,7 @@
       classConsumer.accept(clazz);
     }
 
-    private ChecksumSupplier getChecksumSupplier(ClassKind classKind) {
+    private ChecksumSupplier getChecksumSupplier(ClassKind<T> classKind) {
       if (application.options.encodeChecksums && classKind == ClassKind.PROGRAM) {
         CRC32 crc = new CRC32();
         crc.update(this.context.classCache, 0, this.context.classCache.length);
@@ -567,7 +566,7 @@
 
   private static class CreateFieldVisitor extends FieldVisitor {
 
-    private final CreateDexClassVisitor parent;
+    private final CreateDexClassVisitor<?> parent;
     private final int access;
     private final String name;
     private final String desc;
@@ -575,8 +574,13 @@
     private FieldTypeSignature fieldSignature;
     private List<DexAnnotation> annotations = null;
 
-    public CreateFieldVisitor(CreateDexClassVisitor parent,
-        int access, String name, String desc, String signature, Object value) {
+    public CreateFieldVisitor(
+        CreateDexClassVisitor<?> parent,
+        int access,
+        String name,
+        String desc,
+        String signature,
+        Object value) {
       super(ASM_VERSION);
       this.parent = parent;
       this.access = access;
@@ -670,9 +674,6 @@
       throw new Unreachable("Unexpected static-value type " + type);
     }
 
-    private void addAnnotation(DexAnnotation annotation) {
-      getAnnotations().add(annotation);
-    }
     private List<DexAnnotation> getAnnotations() {
       if (annotations == null) {
         annotations = new ArrayList<>();
@@ -684,7 +685,7 @@
   private static class CreateMethodVisitor extends MethodVisitor {
 
     private final String name;
-    final CreateDexClassVisitor parent;
+    final CreateDexClassVisitor<?> parent;
     private final int parameterCount;
     private List<DexAnnotation> annotations = null;
     private DexValue defaultAnnotation = null;
@@ -698,8 +699,13 @@
     final boolean deprecated;
     Code code = null;
 
-    public CreateMethodVisitor(int access, String name, String desc, String signature,
-        String[] exceptions, CreateDexClassVisitor parent) {
+    public CreateMethodVisitor(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        String[] exceptions,
+        CreateDexClassVisitor<?> parent) {
       super(ASM_VERSION);
       this.name = name;
       this.parent = parent;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 0159139..e14573d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -183,7 +183,7 @@
   }
 
   private DexClass generateTypeWrapper(
-      ClassKind classKind, DexClass dexClass, DexType typeWrapperType) {
+      ClassKind<?> classKind, DexClass dexClass, DexType typeWrapperType) {
     DexType type = dexClass.type;
     DexEncodedField wrapperField = synthesizeWrappedValueEncodedField(typeWrapperType, type);
     return synthesizeWrapper(
@@ -196,7 +196,7 @@
   }
 
   private DexClass generateVivifiedTypeWrapper(
-      ClassKind classKind, DexClass dexClass, DexType vivifiedTypeWrapperType) {
+      ClassKind<?> classKind, DexClass dexClass, DexType vivifiedTypeWrapperType) {
     DexType type = dexClass.type;
     DexEncodedField wrapperField =
         synthesizeWrappedValueEncodedField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
@@ -210,7 +210,7 @@
   }
 
   private DexClass synthesizeWrapper(
-      ClassKind classKind,
+      ClassKind<?> classKind,
       DexType wrappingType,
       DexClass clazz,
       DexEncodedMethod[] virtualMethods,
@@ -398,7 +398,7 @@
           for (DexEncodedMethod alreadyImplementedMethod : implementedMethods) {
             if (alreadyImplementedMethod.method.match(virtualMethod.method)) {
               alreadyAdded = true;
-              continue;
+              break;
             }
           }
           if (!alreadyAdded) {
@@ -503,7 +503,7 @@
   }
 
   private void generateWrappers(
-      ClassKind classKind,
+      ClassKind<?> classKind,
       Set<DexType> synthesized,
       BiConsumer<DexType, DexClass> generatedCallback) {
     while (synthesized.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 474f5d0..3c7b99b 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -71,10 +71,8 @@
    */
   abstract Supplier<T> getTransparentSupplier(T clazz);
 
-  /**
-   * Kind of the classes supported by this collection.
-   */
-  abstract ClassKind getClassKind();
+  /** Kind of the classes supported by this collection. */
+  abstract ClassKind<?> getClassKind();
 
   @Override
   public String toString() {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassProvider.java b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
index 40d5087..c048529 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
@@ -23,14 +23,14 @@
 
 /** Represents a provider for classes loaded from different sources. */
 public abstract class ClassProvider<T extends DexClass> {
-  private final ClassKind classKind;
+  private final ClassKind<T> classKind;
 
-  ClassProvider(ClassKind classKind) {
+  ClassProvider(ClassKind<T> classKind) {
     this.classKind = classKind;
   }
 
   /** The kind of the classes created by the provider. */
-  final ClassKind getClassKind() {
+  final ClassKind<T> getClassKind() {
     return classKind;
   }
 
@@ -56,13 +56,13 @@
 
   /** Create class provider for java class resource provider. */
   public static <T extends DexClass> ClassProvider<T> forClassFileResources(
-      ClassKind classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
+      ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
     return new ClassFileResourceReader<>(classKind, provider, reader);
   }
 
   /** Create class provider for preloaded classes, classes may have conflicting names. */
   public static <T extends DexClass> ClassProvider<T> forPreloadedClasses(
-      ClassKind classKind, Collection<T> classes) {
+      ClassKind<T> classKind, Collection<T> classes) {
     ImmutableListMultimap.Builder<DexType, T> builder = ImmutableListMultimap.builder();
     for (T clazz : classes) {
       builder.put(clazz.type, clazz);
@@ -72,17 +72,17 @@
 
   /** Create class provider for preloaded classes. */
   public static <T extends DexClass> ClassProvider<T> combine(
-      ClassKind classKind, List<ClassProvider<T>> providers) {
+      ClassKind<T> classKind, List<ClassProvider<T>> providers) {
     return new CombinedClassProvider<>(classKind, providers);
   }
 
   private static class ClassFileResourceReader<T extends DexClass> extends ClassProvider<T> {
-    private final ClassKind classKind;
+    private final ClassKind<T> classKind;
     private final ClassFileResourceProvider provider;
     private final JarApplicationReader reader;
 
     private ClassFileResourceReader(
-        ClassKind classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
+        ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
       super(classKind);
       this.classKind = classKind;
       this.provider = provider;
@@ -95,9 +95,9 @@
       ProgramResource resource = provider.getProgramResource(descriptor);
       if (resource != null) {
         try {
-          JarClassFileReader classReader =
-              new JarClassFileReader(reader, classKind.bridgeConsumer(classConsumer));
-          classReader.read(resource, classKind);
+          JarClassFileReader<T> classReader =
+              new JarClassFileReader<>(reader, classConsumer, classKind);
+          classReader.read(resource);
         } catch (ResourceException e) {
           throw new CompilationError("Failed to load class: " + descriptor, e);
         }
@@ -122,7 +122,7 @@
   private static class PreloadedClassProvider<T extends DexClass> extends ClassProvider<T> {
     private final Multimap<DexType, T> classes;
 
-    private PreloadedClassProvider(ClassKind classKind, Multimap<DexType, T> classes) {
+    private PreloadedClassProvider(ClassKind<T> classKind, Multimap<DexType, T> classes) {
       super(classKind);
       this.classes = classes;
     }
@@ -148,7 +148,7 @@
   private static class CombinedClassProvider<T extends DexClass> extends ClassProvider<T> {
     private final List<ClassProvider<T>> providers;
 
-    private CombinedClassProvider(ClassKind classKind, List<ClassProvider<T>> providers) {
+    private CombinedClassProvider(ClassKind<T> classKind, List<ClassProvider<T>> providers) {
       super(classKind);
       this.providers = providers;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
index 45002d0..28eb17b 100644
--- a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
@@ -25,7 +25,7 @@
   }
 
   @Override
-  ClassKind getClassKind() {
+  ClassKind<DexClasspathClass> getClassKind() {
     return ClassKind.CLASSPATH;
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
index 863cce0..d661a87 100644
--- a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
@@ -30,7 +30,7 @@
   }
 
   @Override
-  ClassKind getClassKind() {
+  ClassKind<DexLibraryClass> getClassKind() {
     return ClassKind.LIBRARY;
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 68fa8f3..fff5425 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -53,7 +53,7 @@
   }
 
   @Override
-  ClassKind getClassKind() {
+  ClassKind<DexProgramClass> getClassKind() {
     return ClassKind.PROGRAM;
   }