[jnigen] Generic support (https://github.com/dart-lang/jnigen/issues/136)

Added support for Generics – Closed https://github.com/dart-lang/jnigen/issues/66, Closed https://github.com/dart-lang/jnigen/issues/73 

* TypeClasses are now public and have `fromRef`
* Casting is possible between different types.
* Nested generic classes are also supported.
diff --git a/pkgs/jni/lib/src/jarray.dart b/pkgs/jni/lib/src/jarray.dart
index 4169167..e24f8ac 100644
--- a/pkgs/jni/lib/src/jarray.dart
+++ b/pkgs/jni/lib/src/jarray.dart
@@ -6,21 +6,31 @@
 
 part of 'types.dart';
 
-class _JArrayType<T> extends JType<JArray<T>> {
+class JArrayType<T> extends JObjType<JArray<T>> {
   final JType<T> elementType;
 
-  const _JArrayType(this.elementType);
+  const JArrayType(this.elementType);
 
   @override
   String get signature => '[${elementType.signature}';
+
+  @override
+  JArray<T> fromRef(Pointer<Void> ref) => JArray.fromRef(elementType, ref);
 }
 
 class JArray<E> extends JObject {
+  final JType<E> elementType;
+
+  @override
+  JArrayType<E> get $type => (_$type ??= type(elementType)) as JArrayType<E>;
+
   /// The type which includes information such as the signature of this class.
-  static JType<JArray<T>> type<T>(JType<T> innerType) => _JArrayType(innerType);
+  static JObjType<JArray<T>> type<T>(JType<T> innerType) =>
+      JArrayType(innerType);
 
   /// Construct a new [JArray] with [reference] as its underlying reference.
-  JArray.fromRef(JArrayPtr reference) : super.fromRef(reference);
+  JArray.fromRef(this.elementType, JArrayPtr reference)
+      : super.fromRef(reference);
 
   /// Creates a [JArray] of the given length from the given [type].
   ///
@@ -29,12 +39,14 @@
     if (type._type == JniCallType.objectType) {
       final clazz = type._getClass();
       final array = JArray<E>.fromRef(
+        type,
         _accessors.newObjectArray(length, clazz.reference, nullptr).checkedRef,
       );
       clazz.delete();
       return array;
     }
     return JArray.fromRef(
+      type,
       _accessors.newPrimitiveArray(length, type._type).checkedRef,
     );
   }
@@ -47,6 +59,7 @@
     assert(!fill.isNull, "fill must not be null.");
     final clazz = fill.getClass();
     final array = JArray<E>.fromRef(
+      fill.$type as JObjType<E>,
       _accessors
           .newObjectArray(length, clazz.reference, fill.reference)
           .checkedRef,
@@ -321,7 +334,10 @@
 
 extension ArrayArray<T> on JArray<JArray<T>> {
   JArray<T> operator [](int index) {
-    return JArray<T>.fromRef(elementAt(index, JniCallType.objectType).object);
+    return JArray<T>.fromRef(
+      (elementType as JArrayType<T>).elementType,
+      elementAt(index, JniCallType.objectType).object,
+    );
   }
 
   void operator []=(int index, JArray<T> value) {
diff --git a/pkgs/jni/lib/src/jobject.dart b/pkgs/jni/lib/src/jobject.dart
index 1ec850a..7477c55 100644
--- a/pkgs/jni/lib/src/jobject.dart
+++ b/pkgs/jni/lib/src/jobject.dart
@@ -4,11 +4,14 @@
 
 part of 'types.dart';
 
-class _JObjectType extends JType<JObject> {
-  const _JObjectType();
+class JObjectType extends JObjType<JObject> {
+  const JObjectType();
 
   @override
   String get signature => "Ljava/lang/Object;";
+
+  @override
+  JObject fromRef(Pointer<Void> ref) => JObject.fromRef(ref);
 }
 
 Pointer<T> _getID<T extends NativeType>(
@@ -123,8 +126,11 @@
 ///
 /// This is the base class for classes generated by `jnigen`.
 class JObject extends JReference {
+  JObjType<JObject>? _$type;
+  JObjType<JObject> get $type => _$type ??= type;
+
   /// The type which includes information such as the signature of this class.
-  static const JType<JObject> type = _JObjectType();
+  static const JObjType<JObject> type = JObjectType();
 
   /// Construct a new [JObject] with [reference] as its underlying reference.
   JObject.fromRef(JObjectPtr reference) : super.fromRef(reference);
@@ -143,8 +149,6 @@
     super.delete();
   }
 
-  // TODO(#55): Support casting JObject subclasses
-
   /// Returns [JniClass] corresponding to concrete class of this object.
   ///
   /// This may be a subclass of compile-time class.
@@ -269,6 +273,17 @@
     final id = getStaticMethodID(name, signature);
     return callStaticMethod<T>(id, args, callType);
   }
+
+  /// Casts this object to another type.
+  T castTo<T extends JObject>(JObjType<T> type, {bool deleteOriginal = false}) {
+    if (deleteOriginal) {
+      _jniClass?.delete();
+      _setAsDeleted();
+      return type.fromRef(reference);
+    }
+    final newRef = _env.NewGlobalRef(reference);
+    return type.fromRef(newRef);
+  }
 }
 
 /// A high level wrapper over a JNI class reference.
diff --git a/pkgs/jni/lib/src/jprimitives.dart b/pkgs/jni/lib/src/jprimitives.dart
index 09ac4ab..c83bf6d 100644
--- a/pkgs/jni/lib/src/jprimitives.dart
+++ b/pkgs/jni/lib/src/jprimitives.dart
@@ -7,11 +7,11 @@
 abstract class JPrimitive {}
 
 abstract class JByte extends JPrimitive {
-  static const JType<JByte> type = _JByteTypeClass();
+  static const type = JByteType();
 }
 
-class _JByteTypeClass extends JType<JByte> {
-  const _JByteTypeClass();
+class JByteType extends JType<JByte> {
+  const JByteType();
 
   @override
   int get _type => JniCallType.byteType;
@@ -21,11 +21,11 @@
 }
 
 abstract class JBoolean extends JPrimitive {
-  static const JType<JBoolean> type = _JBooleanType();
+  static const type = JBooleanType();
 }
 
-class _JBooleanType extends JType<JBoolean> {
-  const _JBooleanType();
+class JBooleanType extends JType<JBoolean> {
+  const JBooleanType();
 
   @override
   int get _type => JniCallType.booleanType;
@@ -35,11 +35,11 @@
 }
 
 abstract class JChar extends JPrimitive {
-  static const JType<JChar> type = _JCharType();
+  static const type = JCharType();
 }
 
-class _JCharType extends JType<JChar> {
-  const _JCharType();
+class JCharType extends JType<JChar> {
+  const JCharType();
 
   @override
   int get _type => JniCallType.charType;
@@ -49,11 +49,11 @@
 }
 
 abstract class JShort extends JPrimitive {
-  static const JType<JShort> type = _JShortType();
+  static const type = JShortType();
 }
 
-class _JShortType extends JType<JShort> {
-  const _JShortType();
+class JShortType extends JType<JShort> {
+  const JShortType();
 
   @override
   int get _type => JniCallType.shortType;
@@ -63,11 +63,11 @@
 }
 
 abstract class JInt extends JPrimitive {
-  static const JType<JInt> type = _JIntType();
+  static const type = JIntType();
 }
 
-class _JIntType extends JType<JInt> {
-  const _JIntType();
+class JIntType extends JType<JInt> {
+  const JIntType();
 
   @override
   int get _type => JniCallType.intType;
@@ -77,11 +77,11 @@
 }
 
 abstract class JLong extends JPrimitive {
-  static const JType<JLong> type = _JLongType();
+  static const type = JLongType();
 }
 
-class _JLongType extends JType<JLong> {
-  const _JLongType();
+class JLongType extends JType<JLong> {
+  const JLongType();
 
   @override
   int get _type => JniCallType.longType;
@@ -91,11 +91,11 @@
 }
 
 abstract class JFloat extends JPrimitive {
-  static const JType<JFloat> type = _JFloatType();
+  static const type = JFloatType();
 }
 
-class _JFloatType extends JType<JFloat> {
-  const _JFloatType();
+class JFloatType extends JType<JFloat> {
+  const JFloatType();
 
   @override
   int get _type => JniCallType.floatType;
@@ -105,11 +105,11 @@
 }
 
 abstract class JDouble extends JPrimitive {
-  static const JType<JDouble> type = _JDoubleType();
+  static const type = JDoubleType();
 }
 
-class _JDoubleType extends JType<JDouble> {
-  const _JDoubleType();
+class JDoubleType extends JType<JDouble> {
+  const JDoubleType();
 
   @override
   int get _type => JniCallType.doubleType;
diff --git a/pkgs/jni/lib/src/jreference.dart b/pkgs/jni/lib/src/jreference.dart
index c115e64..1bd6c5c 100644
--- a/pkgs/jni/lib/src/jreference.dart
+++ b/pkgs/jni/lib/src/jreference.dart
@@ -25,14 +25,18 @@
   /// Returns whether this object is deleted.
   bool get isDeleted => _deleted;
 
-  /// Deletes the underlying JNI reference. Further uses will throw
-  /// [UseAfterFreeException].
-  void delete() {
+  void _setAsDeleted() {
     if (_deleted) {
       throw DoubleFreeException(this, reference);
     }
     _deleted = true;
     _finalizer.detach(this);
+  }
+
+  /// Deletes the underlying JNI reference. Further uses will throw
+  /// [UseAfterFreeException].
+  void delete() {
+    _setAsDeleted();
     _env.DeleteGlobalRef(reference);
   }
 
diff --git a/pkgs/jni/lib/src/jstring.dart b/pkgs/jni/lib/src/jstring.dart
index 66b1935..5746e23 100644
--- a/pkgs/jni/lib/src/jstring.dart
+++ b/pkgs/jni/lib/src/jstring.dart
@@ -4,16 +4,22 @@
 
 part of 'types.dart';
 
-class _JStringType extends JType<JString> {
-  const _JStringType();
+class JStringType extends JObjType<JString> {
+  const JStringType();
 
   @override
   String get signature => "Ljava/lang/String;";
+
+  @override
+  JString fromRef(Pointer<Void> ref) => JString.fromRef(ref);
 }
 
 class JString extends JObject {
+  @override
+  JObjType<JObject> get $type => _$type ??= type;
+
   /// The type which includes information such as the signature of this class.
-  static const JType<JString> type = _JStringType();
+  static const JObjType<JString> type = JStringType();
 
   /// Construct a new [JString] with [reference] as its underlying reference.
   JString.fromRef(JStringPtr reference) : super.fromRef(reference);
diff --git a/pkgs/jni/lib/src/types.dart b/pkgs/jni/lib/src/types.dart
index c9265a7..b1f4a77 100644
--- a/pkgs/jni/lib/src/types.dart
+++ b/pkgs/jni/lib/src/types.dart
@@ -28,9 +28,19 @@
 abstract class JType<T> {
   const JType();
 
-  int get _type => JniCallType.objectType;
+  int get _type;
 
   String get signature;
 
   JniClass _getClass() => Jni.findJniClass(signature);
 }
+
+abstract class JObjType<T extends JObject> extends JType<T> {
+  const JObjType();
+
+  @override
+  int get _type => JniCallType.objectType;
+
+  /// Creates an object from this type using the reference.
+  T fromRef(Pointer<Void> ref);
+}
diff --git a/pkgs/jni/test/jobject_test.dart b/pkgs/jni/test/jobject_test.dart
index 9722c7c..5bafe75 100644
--- a/pkgs/jni/test/jobject_test.dart
+++ b/pkgs/jni/test/jobject_test.dart
@@ -191,6 +191,18 @@
         .use((f) => f.callMethodByName<int>("ordinal", "()I", []));
     expect(ordinal, equals(1));
   });
+  test("casting", () {
+    using((arena) {
+      final str = "hello".toJString()..deletedIn(arena);
+      final obj = str.castTo(JObject.type)..deletedIn(arena);
+      final backToStr = obj.castTo(JString.type);
+      expect(backToStr.toDartString(), str.toDartString());
+      final _ = backToStr.castTo(JObject.type, deleteOriginal: true)
+        ..deletedIn(arena);
+      expect(backToStr.toDartString, throwsA(isA<UseAfterFreeException>()));
+      expect(backToStr.delete, throwsA(isA<DoubleFreeException>()));
+    });
+  });
 
   test("Isolate", () {
     Isolate.spawn(doSomeWorkInIsolate, null);
diff --git a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
index 9bc9ef6..1b6cc08 100644
--- a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
+++ b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
@@ -7,6 +7,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -22,10 +23,17 @@
 
 /// from: com.example.in_app_java.AndroidUtils
 class AndroidUtils extends jni.JObject {
-  AndroidUtils.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  AndroidUtils.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<AndroidUtils> type = _$AndroidUtilsType();
+  static const type = $AndroidUtilsType();
+
   static final _showToast = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(ffi.Pointer<ffi.Void>,
@@ -40,17 +48,20 @@
       _showToast(mainActivity.reference, text.reference, duration).check();
 }
 
-class _$AndroidUtilsType extends jni.JType<AndroidUtils> {
-  const _$AndroidUtilsType();
+class $AndroidUtilsType extends jni.JObjType<AndroidUtils> {
+  const $AndroidUtilsType();
 
   @override
   String get signature => r"Lcom/example/in_app_java/AndroidUtils;";
+
+  @override
+  AndroidUtils fromRef(jni.JObjectPtr ref) => AndroidUtils.fromRef(ref);
 }
 
 extension $AndroidUtilsArray on jni.JArray<AndroidUtils> {
   AndroidUtils operator [](int index) {
-    return AndroidUtils.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $AndroidUtilsType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, AndroidUtils value) {
@@ -60,10 +71,17 @@
 
 /// from: android.os.Build
 class Build extends jni.JObject {
-  Build.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Build.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<Build> type = _$BuildType();
+  static const type = $BuildType();
+
   static final _get_BOARD =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "get_Build__BOARD")
@@ -71,7 +89,8 @@
 
   /// from: static public final java.lang.String BOARD
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get BOARD => jni.JString.fromRef(_get_BOARD().object);
+  static jni.JString get BOARD =>
+      const jni.JStringType().fromRef(_get_BOARD().object);
 
   static final _get_BOOTLOADER =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -81,7 +100,7 @@
   /// from: static public final java.lang.String BOOTLOADER
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get BOOTLOADER =>
-      jni.JString.fromRef(_get_BOOTLOADER().object);
+      const jni.JStringType().fromRef(_get_BOOTLOADER().object);
 
   static final _get_BRAND =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -90,7 +109,8 @@
 
   /// from: static public final java.lang.String BRAND
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get BRAND => jni.JString.fromRef(_get_BRAND().object);
+  static jni.JString get BRAND =>
+      const jni.JStringType().fromRef(_get_BRAND().object);
 
   static final _get_CPU_ABI =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -99,7 +119,8 @@
 
   /// from: static public final java.lang.String CPU_ABI
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get CPU_ABI => jni.JString.fromRef(_get_CPU_ABI().object);
+  static jni.JString get CPU_ABI =>
+      const jni.JStringType().fromRef(_get_CPU_ABI().object);
 
   static final _get_CPU_ABI2 =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -109,7 +130,7 @@
   /// from: static public final java.lang.String CPU_ABI2
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get CPU_ABI2 =>
-      jni.JString.fromRef(_get_CPU_ABI2().object);
+      const jni.JStringType().fromRef(_get_CPU_ABI2().object);
 
   static final _get_DEVICE =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -118,7 +139,8 @@
 
   /// from: static public final java.lang.String DEVICE
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get DEVICE => jni.JString.fromRef(_get_DEVICE().object);
+  static jni.JString get DEVICE =>
+      const jni.JStringType().fromRef(_get_DEVICE().object);
 
   static final _get_DISPLAY =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -127,7 +149,8 @@
 
   /// from: static public final java.lang.String DISPLAY
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get DISPLAY => jni.JString.fromRef(_get_DISPLAY().object);
+  static jni.JString get DISPLAY =>
+      const jni.JStringType().fromRef(_get_DISPLAY().object);
 
   static final _get_FINGERPRINT =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -137,7 +160,7 @@
   /// from: static public final java.lang.String FINGERPRINT
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get FINGERPRINT =>
-      jni.JString.fromRef(_get_FINGERPRINT().object);
+      const jni.JStringType().fromRef(_get_FINGERPRINT().object);
 
   static final _get_HARDWARE =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -147,7 +170,7 @@
   /// from: static public final java.lang.String HARDWARE
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get HARDWARE =>
-      jni.JString.fromRef(_get_HARDWARE().object);
+      const jni.JStringType().fromRef(_get_HARDWARE().object);
 
   static final _get_HOST =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__HOST")
@@ -155,7 +178,8 @@
 
   /// from: static public final java.lang.String HOST
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get HOST => jni.JString.fromRef(_get_HOST().object);
+  static jni.JString get HOST =>
+      const jni.JStringType().fromRef(_get_HOST().object);
 
   static final _get_ID =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__ID")
@@ -163,7 +187,8 @@
 
   /// from: static public final java.lang.String ID
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get ID => jni.JString.fromRef(_get_ID().object);
+  static jni.JString get ID =>
+      const jni.JStringType().fromRef(_get_ID().object);
 
   static final _get_MANUFACTURER =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -173,7 +198,7 @@
   /// from: static public final java.lang.String MANUFACTURER
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get MANUFACTURER =>
-      jni.JString.fromRef(_get_MANUFACTURER().object);
+      const jni.JStringType().fromRef(_get_MANUFACTURER().object);
 
   static final _get_MODEL =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -182,7 +207,8 @@
 
   /// from: static public final java.lang.String MODEL
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get MODEL => jni.JString.fromRef(_get_MODEL().object);
+  static jni.JString get MODEL =>
+      const jni.JStringType().fromRef(_get_MODEL().object);
 
   static final _get_ODM_SKU =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -191,7 +217,8 @@
 
   /// from: static public final java.lang.String ODM_SKU
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get ODM_SKU => jni.JString.fromRef(_get_ODM_SKU().object);
+  static jni.JString get ODM_SKU =>
+      const jni.JStringType().fromRef(_get_ODM_SKU().object);
 
   static final _get_PRODUCT =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -200,7 +227,8 @@
 
   /// from: static public final java.lang.String PRODUCT
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get PRODUCT => jni.JString.fromRef(_get_PRODUCT().object);
+  static jni.JString get PRODUCT =>
+      const jni.JStringType().fromRef(_get_PRODUCT().object);
 
   static final _get_RADIO =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -209,7 +237,8 @@
 
   /// from: static public final java.lang.String RADIO
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get RADIO => jni.JString.fromRef(_get_RADIO().object);
+  static jni.JString get RADIO =>
+      const jni.JStringType().fromRef(_get_RADIO().object);
 
   static final _get_SERIAL =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -218,7 +247,8 @@
 
   /// from: static public final java.lang.String SERIAL
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get SERIAL => jni.JString.fromRef(_get_SERIAL().object);
+  static jni.JString get SERIAL =>
+      const jni.JStringType().fromRef(_get_SERIAL().object);
 
   static final _get_SKU =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__SKU")
@@ -226,7 +256,8 @@
 
   /// from: static public final java.lang.String SKU
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get SKU => jni.JString.fromRef(_get_SKU().object);
+  static jni.JString get SKU =>
+      const jni.JStringType().fromRef(_get_SKU().object);
 
   static final _get_SOC_MANUFACTURER =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -236,7 +267,7 @@
   /// from: static public final java.lang.String SOC_MANUFACTURER
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get SOC_MANUFACTURER =>
-      jni.JString.fromRef(_get_SOC_MANUFACTURER().object);
+      const jni.JStringType().fromRef(_get_SOC_MANUFACTURER().object);
 
   static final _get_SOC_MODEL =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -246,7 +277,7 @@
   /// from: static public final java.lang.String SOC_MODEL
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString get SOC_MODEL =>
-      jni.JString.fromRef(_get_SOC_MODEL().object);
+      const jni.JStringType().fromRef(_get_SOC_MODEL().object);
 
   static final _get_SUPPORTED_32_BIT_ABIS =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -256,7 +287,8 @@
   /// from: static public final java.lang.String[] SUPPORTED_32_BIT_ABIS
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<jni.JString> get SUPPORTED_32_BIT_ABIS =>
-      jni.JArray<jni.JString>.fromRef(_get_SUPPORTED_32_BIT_ABIS().object);
+      const jni.JArrayType(jni.JStringType())
+          .fromRef(_get_SUPPORTED_32_BIT_ABIS().object);
 
   static final _get_SUPPORTED_64_BIT_ABIS =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -266,7 +298,8 @@
   /// from: static public final java.lang.String[] SUPPORTED_64_BIT_ABIS
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<jni.JString> get SUPPORTED_64_BIT_ABIS =>
-      jni.JArray<jni.JString>.fromRef(_get_SUPPORTED_64_BIT_ABIS().object);
+      const jni.JArrayType(jni.JStringType())
+          .fromRef(_get_SUPPORTED_64_BIT_ABIS().object);
 
   static final _get_SUPPORTED_ABIS =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -276,7 +309,8 @@
   /// from: static public final java.lang.String[] SUPPORTED_ABIS
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<jni.JString> get SUPPORTED_ABIS =>
-      jni.JArray<jni.JString>.fromRef(_get_SUPPORTED_ABIS().object);
+      const jni.JArrayType(jni.JStringType())
+          .fromRef(_get_SUPPORTED_ABIS().object);
 
   static final _get_TAGS =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__TAGS")
@@ -284,7 +318,8 @@
 
   /// from: static public final java.lang.String TAGS
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get TAGS => jni.JString.fromRef(_get_TAGS().object);
+  static jni.JString get TAGS =>
+      const jni.JStringType().fromRef(_get_TAGS().object);
 
   static final _get_TIME =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("get_Build__TIME")
@@ -299,7 +334,8 @@
 
   /// from: static public final java.lang.String TYPE
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get TYPE => jni.JString.fromRef(_get_TYPE().object);
+  static jni.JString get TYPE =>
+      const jni.JStringType().fromRef(_get_TYPE().object);
 
   /// from: static public final java.lang.String UNKNOWN
   static const UNKNOWN = "unknown";
@@ -310,7 +346,8 @@
 
   /// from: static public final java.lang.String USER
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString get USER => jni.JString.fromRef(_get_USER().object);
+  static jni.JString get USER =>
+      const jni.JStringType().fromRef(_get_USER().object);
 
   static final _ctor =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Build__ctor")
@@ -326,7 +363,8 @@
 
   /// from: static public java.lang.String getSerial()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static jni.JString getSerial() => jni.JString.fromRef(_getSerial().object);
+  static jni.JString getSerial() =>
+      const jni.JStringType().fromRef(_getSerial().object);
 
   static final _getFingerprintedPartitions =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -336,7 +374,7 @@
   /// from: static public java.util.List getFingerprintedPartitions()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JObject getFingerprintedPartitions() =>
-      jni.JObject.fromRef(_getFingerprintedPartitions().object);
+      const jni.JObjectType().fromRef(_getFingerprintedPartitions().object);
 
   static final _getRadioVersion =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
@@ -346,19 +384,23 @@
   /// from: static public java.lang.String getRadioVersion()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JString getRadioVersion() =>
-      jni.JString.fromRef(_getRadioVersion().object);
+      const jni.JStringType().fromRef(_getRadioVersion().object);
 }
 
-class _$BuildType extends jni.JType<Build> {
-  const _$BuildType();
+class $BuildType extends jni.JObjType<Build> {
+  const $BuildType();
 
   @override
   String get signature => r"Landroid/os/Build;";
+
+  @override
+  Build fromRef(jni.JObjectPtr ref) => Build.fromRef(ref);
 }
 
 extension $BuildArray on jni.JArray<Build> {
   Build operator [](int index) {
-    return Build.fromRef(elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $BuildType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, Build value) {
diff --git a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
index 0bb75e9..a8ccd10 100644
--- a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
+++ b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
@@ -11,6 +11,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -26,10 +27,17 @@
 
 /// from: com.example.notification_plugin.Notifications
 class Notifications extends jni.JObject {
-  Notifications.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Notifications.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<Notifications> type = _$NotificationsType();
+  static const type = $NotificationsType();
+
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "Notifications__ctor")
       .asFunction<jni.JniResult Function()>();
@@ -56,17 +64,20 @@
           .check();
 }
 
-class _$NotificationsType extends jni.JType<Notifications> {
-  const _$NotificationsType();
+class $NotificationsType extends jni.JObjType<Notifications> {
+  const $NotificationsType();
 
   @override
   String get signature => r"Lcom/example/notification_plugin/Notifications;";
+
+  @override
+  Notifications fromRef(jni.JObjectPtr ref) => Notifications.fromRef(ref);
 }
 
 extension $NotificationsArray on jni.JArray<Notifications> {
   Notifications operator [](int index) {
-    return Notifications.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $NotificationsType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, Notifications value) {
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
index 1e827e3..4f9512e 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
@@ -25,6 +25,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -42,10 +43,17 @@
 /// The \#close() method must be called once the document is no longer needed.
 ///@author Ben Litchfield
 class PDDocument extends jni.JObject {
-  PDDocument.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  PDDocument.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<PDDocument> type = _$PDDocumentType();
+  static const type = $PDDocumentType();
+
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "PDDocument__ctor")
       .asFunction<jni.JniResult Function()>();
@@ -330,8 +338,8 @@
   ///@param page The page to import.
   ///@return The page that was imported.
   ///@throws IOException If there is an error copying the page.
-  jni.JObject importPage(jni.JObject page) =>
-      jni.JObject.fromRef(_importPage(reference, page.reference).object);
+  jni.JObject importPage(jni.JObject page) => const jni.JObjectType()
+      .fromRef(_importPage(reference, page.reference).object);
 
   static final _getDocument = jniLookup<
           ffi.NativeFunction<
@@ -345,7 +353,7 @@
   /// This will get the low level document.
   ///@return The document that this layer sits on top of.
   jni.JObject getDocument() =>
-      jni.JObject.fromRef(_getDocument(reference).object);
+      const jni.JObjectType().fromRef(_getDocument(reference).object);
 
   static final _getDocumentInformation = jniLookup<
           ffi.NativeFunction<
@@ -364,8 +372,8 @@
   /// PDDocumentCatalog\#getMetadata().
   ///@return The documents /Info dictionary, never null.
   pddocumentinformation_.PDDocumentInformation getDocumentInformation() =>
-      pddocumentinformation_.PDDocumentInformation.fromRef(
-          _getDocumentInformation(reference).object);
+      const pddocumentinformation_.$PDDocumentInformationType()
+          .fromRef(_getDocumentInformation(reference).object);
 
   static final _setDocumentInformation = jniLookup<
           ffi.NativeFunction<
@@ -399,7 +407,7 @@
   /// This will get the document CATALOG. This is guaranteed to not return null.
   ///@return The documents /Root dictionary
   jni.JObject getDocumentCatalog() =>
-      jni.JObject.fromRef(_getDocumentCatalog(reference).object);
+      const jni.JObjectType().fromRef(_getDocumentCatalog(reference).object);
 
   static final _isEncrypted = jniLookup<
           ffi.NativeFunction<
@@ -428,7 +436,7 @@
   /// PDStandardEncryption object.
   ///@return The encryption dictionary(most likely a PDStandardEncryption object)
   jni.JObject getEncryption() =>
-      jni.JObject.fromRef(_getEncryption(reference).object);
+      const jni.JObjectType().fromRef(_getEncryption(reference).object);
 
   static final _setEncryptionDictionary = jniLookup<
               ffi.NativeFunction<
@@ -460,8 +468,8 @@
   /// last in time when empty signature fields are created first but signed after other fields.
   ///@return the last signature as <code>PDSignatureField</code>.
   ///@throws IOException if no document catalog can be found.
-  jni.JObject getLastSignatureDictionary() =>
-      jni.JObject.fromRef(_getLastSignatureDictionary(reference).object);
+  jni.JObject getLastSignatureDictionary() => const jni.JObjectType()
+      .fromRef(_getLastSignatureDictionary(reference).object);
 
   static final _getSignatureFields = jniLookup<
           ffi.NativeFunction<
@@ -476,7 +484,7 @@
   ///@return a <code>List</code> of <code>PDSignatureField</code>s
   ///@throws IOException if no document catalog can be found.
   jni.JObject getSignatureFields() =>
-      jni.JObject.fromRef(_getSignatureFields(reference).object);
+      jni.JObjectType().fromRef(_getSignatureFields(reference).object);
 
   static final _getSignatureDictionaries = jniLookup<
               ffi.NativeFunction<
@@ -491,7 +499,7 @@
   ///@return a <code>List</code> of <code>PDSignatureField</code>s
   ///@throws IOException if no document catalog can be found.
   jni.JObject getSignatureDictionaries() =>
-      jni.JObject.fromRef(_getSignatureDictionaries(reference).object);
+      jni.JObjectType().fromRef(_getSignatureDictionaries(reference).object);
 
   static final _registerTrueTypeFontForClosing = jniLookup<
               ffi.NativeFunction<
@@ -526,7 +534,7 @@
   ///@throws InvalidPasswordException If the file required a non-empty password.
   ///@throws IOException in case of a file reading or parsing error
   static PDDocument load(jni.JObject file) =>
-      PDDocument.fromRef(_load(file.reference).object);
+      const $PDDocumentType().fromRef(_load(file.reference).object);
 
   static final _load1 = jniLookup<
           ffi.NativeFunction<
@@ -546,8 +554,8 @@
   ///@throws InvalidPasswordException If the file required a non-empty password.
   ///@throws IOException in case of a file reading or parsing error
   static PDDocument load1(jni.JObject file, jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(
-          _load1(file.reference, memUsageSetting.reference).object);
+      const $PDDocumentType()
+          .fromRef(_load1(file.reference, memUsageSetting.reference).object);
 
   static final _load2 = jniLookup<
           ffi.NativeFunction<
@@ -567,7 +575,8 @@
   ///@throws InvalidPasswordException If the password is incorrect.
   ///@throws IOException in case of a file reading or parsing error
   static PDDocument load2(jni.JObject file, jni.JString password) =>
-      PDDocument.fromRef(_load2(file.reference, password.reference).object);
+      const $PDDocumentType()
+          .fromRef(_load2(file.reference, password.reference).object);
 
   static final _load3 = jniLookup<
           ffi.NativeFunction<
@@ -591,7 +600,7 @@
   ///@throws IOException in case of a file reading or parsing error
   static PDDocument load3(jni.JObject file, jni.JString password,
           jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(
+      const $PDDocumentType().fromRef(
           _load3(file.reference, password.reference, memUsageSetting.reference)
               .object);
 
@@ -618,7 +627,7 @@
   ///@throws IOException in case of a file reading or parsing error
   static PDDocument load4(jni.JObject file, jni.JString password,
           jni.JObject keyStore, jni.JString alias) =>
-      PDDocument.fromRef(_load4(file.reference, password.reference,
+      const $PDDocumentType().fromRef(_load4(file.reference, password.reference,
               keyStore.reference, alias.reference)
           .object);
 
@@ -655,7 +664,7 @@
           jni.JObject keyStore,
           jni.JString alias,
           jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(_load5(file.reference, password.reference,
+      const $PDDocumentType().fromRef(_load5(file.reference, password.reference,
               keyStore.reference, alias.reference, memUsageSetting.reference)
           .object);
 
@@ -675,7 +684,7 @@
   ///@throws InvalidPasswordException If the PDF required a non-empty password.
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load6(jni.JObject input) =>
-      PDDocument.fromRef(_load6(input.reference).object);
+      const $PDDocumentType().fromRef(_load6(input.reference).object);
 
   static final _load7 = jniLookup<
           ffi.NativeFunction<
@@ -696,8 +705,8 @@
   ///@throws InvalidPasswordException If the PDF required a non-empty password.
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load7(jni.JObject input, jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(
-          _load7(input.reference, memUsageSetting.reference).object);
+      const $PDDocumentType()
+          .fromRef(_load7(input.reference, memUsageSetting.reference).object);
 
   static final _load8 = jniLookup<
           ffi.NativeFunction<
@@ -718,7 +727,8 @@
   ///@throws InvalidPasswordException If the password is incorrect.
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load8(jni.JObject input, jni.JString password) =>
-      PDDocument.fromRef(_load8(input.reference, password.reference).object);
+      const $PDDocumentType()
+          .fromRef(_load8(input.reference, password.reference).object);
 
   static final _load9 = jniLookup<
           ffi.NativeFunction<
@@ -744,8 +754,8 @@
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load9(jni.JObject input, jni.JString password,
           jni.JObject keyStore, jni.JString alias) =>
-      PDDocument.fromRef(_load9(input.reference, password.reference,
-              keyStore.reference, alias.reference)
+      const $PDDocumentType().fromRef(_load9(input.reference,
+              password.reference, keyStore.reference, alias.reference)
           .object);
 
   static final _load10 = jniLookup<
@@ -771,7 +781,7 @@
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load10(jni.JObject input, jni.JString password,
           jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(_load10(
+      const $PDDocumentType().fromRef(_load10(
               input.reference, password.reference, memUsageSetting.reference)
           .object);
 
@@ -810,8 +820,12 @@
           jni.JObject keyStore,
           jni.JString alias,
           jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(_load11(input.reference, password.reference,
-              keyStore.reference, alias.reference, memUsageSetting.reference)
+      const $PDDocumentType().fromRef(_load11(
+              input.reference,
+              password.reference,
+              keyStore.reference,
+              alias.reference,
+              memUsageSetting.reference)
           .object);
 
   static final _load12 = jniLookup<
@@ -829,7 +843,7 @@
   ///@throws InvalidPasswordException If the PDF required a non-empty password.
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load12(jni.JArray<jni.JByte> input) =>
-      PDDocument.fromRef(_load12(input.reference).object);
+      const $PDDocumentType().fromRef(_load12(input.reference).object);
 
   static final _load13 = jniLookup<
           ffi.NativeFunction<
@@ -849,7 +863,8 @@
   ///@throws InvalidPasswordException If the password is incorrect.
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load13(jni.JArray<jni.JByte> input, jni.JString password) =>
-      PDDocument.fromRef(_load13(input.reference, password.reference).object);
+      const $PDDocumentType()
+          .fromRef(_load13(input.reference, password.reference).object);
 
   static final _load14 = jniLookup<
           ffi.NativeFunction<
@@ -875,8 +890,8 @@
   ///@throws IOException In case of a reading or parsing error.
   static PDDocument load14(jni.JArray<jni.JByte> input, jni.JString password,
           jni.JObject keyStore, jni.JString alias) =>
-      PDDocument.fromRef(_load14(input.reference, password.reference,
-              keyStore.reference, alias.reference)
+      const $PDDocumentType().fromRef(_load14(input.reference,
+              password.reference, keyStore.reference, alias.reference)
           .object);
 
   static final _load15 = jniLookup<
@@ -913,8 +928,12 @@
           jni.JObject keyStore,
           jni.JString alias,
           jni.JObject memUsageSetting) =>
-      PDDocument.fromRef(_load15(input.reference, password.reference,
-              keyStore.reference, alias.reference, memUsageSetting.reference)
+      const $PDDocumentType().fromRef(_load15(
+              input.reference,
+              password.reference,
+              keyStore.reference,
+              alias.reference,
+              memUsageSetting.reference)
           .object);
 
   static final _save = jniLookup<
@@ -1086,7 +1105,7 @@
   ///@throws IllegalStateException if the document was not loaded from a file or a stream or
   /// signature options were not set.
   jni.JObject saveIncrementalForExternalSigning(jni.JObject output) =>
-      jni.JObject.fromRef(
+      const jni.JObjectType().fromRef(
           _saveIncrementalForExternalSigning(reference, output.reference)
               .object);
 
@@ -1107,7 +1126,7 @@
   ///@param pageIndex the 0-based page index
   ///@return the page at the given index.
   jni.JObject getPage(int pageIndex) =>
-      jni.JObject.fromRef(_getPage(reference, pageIndex).object);
+      const jni.JObjectType().fromRef(_getPage(reference, pageIndex).object);
 
   static final _getPages = jniLookup<
           ffi.NativeFunction<
@@ -1120,7 +1139,8 @@
   ///
   /// Returns the page tree.
   ///@return the page tree
-  jni.JObject getPages() => jni.JObject.fromRef(_getPages(reference).object);
+  jni.JObject getPages() =>
+      const jni.JObjectType().fromRef(_getPages(reference).object);
 
   static final _getNumberOfPages = jniLookup<
           ffi.NativeFunction<
@@ -1183,8 +1203,8 @@
   /// only mode so that permissions cannot be changed. Methods providing access to content should rely on this object
   /// to verify if the current user is allowed to proceed.
   ///@return the access permissions for the current user on the document.
-  jni.JObject getCurrentAccessPermission() =>
-      jni.JObject.fromRef(_getCurrentAccessPermission(reference).object);
+  jni.JObject getCurrentAccessPermission() => const jni.JObjectType()
+      .fromRef(_getCurrentAccessPermission(reference).object);
 
   static final _isAllSecurityToBeRemoved = jniLookup<
               ffi.NativeFunction<
@@ -1224,7 +1244,7 @@
   /// Provides the document ID.
   ///@return the document ID
   jni.JObject getDocumentId() =>
-      jni.JObject.fromRef(_getDocumentId(reference).object);
+      const jni.JObjectType().fromRef(_getDocumentId(reference).object);
 
   static final _setDocumentId = jniLookup<
           ffi.NativeFunction<
@@ -1278,7 +1298,7 @@
   /// Returns the resource cache associated with this document, or null if there is none.
   ///@return the resource cache or null.
   jni.JObject getResourceCache() =>
-      jni.JObject.fromRef(_getResourceCache(reference).object);
+      const jni.JObjectType().fromRef(_getResourceCache(reference).object);
 
   static final _setResourceCache = jniLookup<
           ffi.NativeFunction<
@@ -1296,17 +1316,20 @@
       _setResourceCache(reference, resourceCache.reference).check();
 }
 
-class _$PDDocumentType extends jni.JType<PDDocument> {
-  const _$PDDocumentType();
+class $PDDocumentType extends jni.JObjType<PDDocument> {
+  const $PDDocumentType();
 
   @override
   String get signature => r"Lorg/apache/pdfbox/pdmodel/PDDocument;";
+
+  @override
+  PDDocument fromRef(jni.JObjectPtr ref) => PDDocument.fromRef(ref);
 }
 
 extension $PDDocumentArray on jni.JArray<PDDocument> {
   PDDocument operator [](int index) {
-    return PDDocument.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $PDDocumentType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, PDDocument value) {
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
index cc07461..f152767 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
@@ -25,6 +25,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -43,11 +44,17 @@
 ///@author Ben Litchfield
 ///@author Gerardo Ortiz
 class PDDocumentInformation extends jni.JObject {
-  PDDocumentInformation.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  PDDocumentInformation.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<PDDocumentInformation> type =
-      _$PDDocumentInformationType();
+  static const type = $PDDocumentInformationType();
+
   static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
           "PDDocumentInformation__ctor")
       .asFunction<jni.JniResult Function()>();
@@ -82,7 +89,7 @@
   /// This will get the underlying dictionary that this object wraps.
   ///@return The underlying info dictionary.
   jni.JObject getCOSObject() =>
-      jni.JObject.fromRef(_getCOSObject(reference).object);
+      const jni.JObjectType().fromRef(_getCOSObject(reference).object);
 
   static final _getPropertyStringValue = jniLookup<
               ffi.NativeFunction<
@@ -105,7 +112,7 @@
   ///@param propertyKey the dictionaries key
   ///@return the properties value
   jni.JObject getPropertyStringValue(jni.JString propertyKey) =>
-      jni.JObject.fromRef(
+      const jni.JObjectType().fromRef(
           _getPropertyStringValue(reference, propertyKey.reference).object);
 
   static final _getTitle = jniLookup<
@@ -119,7 +126,8 @@
   ///
   /// This will get the title of the document.  This will return null if no title exists.
   ///@return The title of the document.
-  jni.JString getTitle() => jni.JString.fromRef(_getTitle(reference).object);
+  jni.JString getTitle() =>
+      const jni.JStringType().fromRef(_getTitle(reference).object);
 
   static final _setTitle = jniLookup<
           ffi.NativeFunction<
@@ -147,7 +155,8 @@
   ///
   /// This will get the author of the document.  This will return null if no author exists.
   ///@return The author of the document.
-  jni.JString getAuthor() => jni.JString.fromRef(_getAuthor(reference).object);
+  jni.JString getAuthor() =>
+      const jni.JStringType().fromRef(_getAuthor(reference).object);
 
   static final _setAuthor = jniLookup<
           ffi.NativeFunction<
@@ -176,7 +185,7 @@
   /// This will get the subject of the document.  This will return null if no subject exists.
   ///@return The subject of the document.
   jni.JString getSubject() =>
-      jni.JString.fromRef(_getSubject(reference).object);
+      const jni.JStringType().fromRef(_getSubject(reference).object);
 
   static final _setSubject = jniLookup<
           ffi.NativeFunction<
@@ -205,7 +214,7 @@
   /// This will get the keywords of the document.  This will return null if no keywords exists.
   ///@return The keywords of the document.
   jni.JString getKeywords() =>
-      jni.JString.fromRef(_getKeywords(reference).object);
+      const jni.JStringType().fromRef(_getKeywords(reference).object);
 
   static final _setKeywords = jniLookup<
           ffi.NativeFunction<
@@ -234,7 +243,7 @@
   /// This will get the creator of the document.  This will return null if no creator exists.
   ///@return The creator of the document.
   jni.JString getCreator() =>
-      jni.JString.fromRef(_getCreator(reference).object);
+      const jni.JStringType().fromRef(_getCreator(reference).object);
 
   static final _setCreator = jniLookup<
           ffi.NativeFunction<
@@ -263,7 +272,7 @@
   /// This will get the producer of the document.  This will return null if no producer exists.
   ///@return The producer of the document.
   jni.JString getProducer() =>
-      jni.JString.fromRef(_getProducer(reference).object);
+      const jni.JStringType().fromRef(_getProducer(reference).object);
 
   static final _setProducer = jniLookup<
           ffi.NativeFunction<
@@ -292,7 +301,7 @@
   /// This will get the creation date of the document.  This will return null if no creation date exists.
   ///@return The creation date of the document.
   jni.JObject getCreationDate() =>
-      jni.JObject.fromRef(_getCreationDate(reference).object);
+      const jni.JObjectType().fromRef(_getCreationDate(reference).object);
 
   static final _setCreationDate = jniLookup<
               ffi.NativeFunction<
@@ -322,7 +331,7 @@
   /// This will get the modification date of the document.  This will return null if no modification date exists.
   ///@return The modification date of the document.
   jni.JObject getModificationDate() =>
-      jni.JObject.fromRef(_getModificationDate(reference).object);
+      const jni.JObjectType().fromRef(_getModificationDate(reference).object);
 
   static final _setModificationDate = jniLookup<
               ffi.NativeFunction<
@@ -353,7 +362,7 @@
   /// This will return null if one is not found.
   ///@return The trapped value for the document.
   jni.JString getTrapped() =>
-      jni.JString.fromRef(_getTrapped(reference).object);
+      const jni.JStringType().fromRef(_getTrapped(reference).object);
 
   static final _getMetadataKeys = jniLookup<
               ffi.NativeFunction<
@@ -368,7 +377,7 @@
   ///@return all metadata key strings.
   ///@since Apache PDFBox 1.3.0
   jni.JObject getMetadataKeys() =>
-      jni.JObject.fromRef(_getMetadataKeys(reference).object);
+      jni.JObjectType().fromRef(_getMetadataKeys(reference).object);
 
   static final _getCustomMetadataValue = jniLookup<
               ffi.NativeFunction<
@@ -387,7 +396,7 @@
   ///@param fieldName Name of custom metadata field from pdf document.
   ///@return String Value of metadata field
   jni.JString getCustomMetadataValue(jni.JString fieldName) =>
-      jni.JString.fromRef(
+      const jni.JStringType().fromRef(
           _getCustomMetadataValue(reference, fieldName.reference).object);
 
   static final _setCustomMetadataValue = jniLookup<
@@ -427,17 +436,21 @@
       _setTrapped(reference, value.reference).check();
 }
 
-class _$PDDocumentInformationType extends jni.JType<PDDocumentInformation> {
-  const _$PDDocumentInformationType();
+class $PDDocumentInformationType extends jni.JObjType<PDDocumentInformation> {
+  const $PDDocumentInformationType();
 
   @override
   String get signature => r"Lorg/apache/pdfbox/pdmodel/PDDocumentInformation;";
+
+  @override
+  PDDocumentInformation fromRef(jni.JObjectPtr ref) =>
+      PDDocumentInformation.fromRef(ref);
 }
 
 extension $PDDocumentInformationArray on jni.JArray<PDDocumentInformation> {
   PDDocumentInformation operator [](int index) {
-    return PDDocumentInformation.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $PDDocumentInformationType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, PDDocumentInformation value) {
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
index e35b19c..45eea01 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
@@ -25,6 +25,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -46,10 +47,17 @@
 /// smaller and smaller chunks of the page. Eventually, we fully process each page and then print it.
 ///@author Ben Litchfield
 class PDFTextStripper extends jni.JObject {
-  PDFTextStripper.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  PDFTextStripper.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<PDFTextStripper> type = _$PDFTextStripperType();
+  static const type = $PDFTextStripperType();
+
   static final _get_LINE_SEPARATOR = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(
@@ -65,7 +73,7 @@
   ///
   /// The platform's line separator.
   jni.JString get LINE_SEPARATOR =>
-      jni.JString.fromRef(_get_LINE_SEPARATOR(reference).object);
+      const jni.JStringType().fromRef(_get_LINE_SEPARATOR(reference).object);
 
   static final _get_charactersByArticle = jniLookup<
           ffi.NativeFunction<
@@ -93,7 +101,7 @@
   ///
   /// Most PDFs won't have any beads, so charactersByArticle will contain a single entry.
   jni.JObject get charactersByArticle =>
-      jni.JObject.fromRef(_get_charactersByArticle(reference).object);
+      jni.JObjectType().fromRef(_get_charactersByArticle(reference).object);
   static final _set_charactersByArticle = jniLookup<
               ffi.NativeFunction<
                   jni.JThrowablePtr Function(
@@ -132,8 +140,8 @@
 
   /// from: protected org.apache.pdfbox.pdmodel.PDDocument document
   /// The returned object must be deleted after use, by calling the `delete` method.
-  pddocument_.PDDocument get document =>
-      pddocument_.PDDocument.fromRef(_get_document(reference).object);
+  pddocument_.PDDocument get document => const pddocument_.$PDDocumentType()
+      .fromRef(_get_document(reference).object);
   static final _set_document = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -158,7 +166,8 @@
 
   /// from: protected java.io.Writer output
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JObject get output => jni.JObject.fromRef(_get_output(reference).object);
+  jni.JObject get output =>
+      const jni.JObjectType().fromRef(_get_output(reference).object);
   static final _set_output = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(jni.JObjectPtr,
@@ -201,8 +210,8 @@
   ///@param doc The document to get the text from.
   ///@return The text of the PDF document.
   ///@throws IOException if the doc state is invalid or it is encrypted.
-  jni.JString getText(pddocument_.PDDocument doc) =>
-      jni.JString.fromRef(_getText(reference, doc.reference).object);
+  jni.JString getText(pddocument_.PDDocument doc) => const jni.JStringType()
+      .fromRef(_getText(reference, doc.reference).object);
 
   static final _writeText = jniLookup<
           ffi.NativeFunction<
@@ -549,7 +558,7 @@
   /// This will get the line separator.
   ///@return The desired line separator string.
   jni.JString getLineSeparator() =>
-      jni.JString.fromRef(_getLineSeparator(reference).object);
+      const jni.JStringType().fromRef(_getLineSeparator(reference).object);
 
   static final _getWordSeparator = jniLookup<
           ffi.NativeFunction<
@@ -563,7 +572,7 @@
   /// This will get the word separator.
   ///@return The desired word separator string.
   jni.JString getWordSeparator() =>
-      jni.JString.fromRef(_getWordSeparator(reference).object);
+      const jni.JStringType().fromRef(_getWordSeparator(reference).object);
 
   static final _setWordSeparator = jniLookup<
           ffi.NativeFunction<
@@ -618,7 +627,8 @@
   ///
   /// The output stream that is being written to.
   ///@return The stream that output is being written to.
-  jni.JObject getOutput() => jni.JObject.fromRef(_getOutput(reference).object);
+  jni.JObject getOutput() =>
+      const jni.JObjectType().fromRef(_getOutput(reference).object);
 
   static final _getCharactersByArticle = jniLookup<
               ffi.NativeFunction<
@@ -633,7 +643,7 @@
   /// returns a List that contains List objects, the inner lists will contain TextPosition objects.
   ///@return A double List of TextPositions for all text strings on the page.
   jni.JObject getCharactersByArticle() =>
-      jni.JObject.fromRef(_getCharactersByArticle(reference).object);
+      jni.JObjectType().fromRef(_getCharactersByArticle(reference).object);
 
   static final _setSuppressDuplicateOverlappingText = jniLookup<
               ffi.NativeFunction<
@@ -691,7 +701,7 @@
   /// Get the bookmark where text extraction should end, inclusive. Default is null.
   ///@return The ending bookmark.
   jni.JObject getEndBookmark() =>
-      jni.JObject.fromRef(_getEndBookmark(reference).object);
+      const jni.JObjectType().fromRef(_getEndBookmark(reference).object);
 
   static final _setEndBookmark = jniLookup<
           ffi.NativeFunction<
@@ -720,7 +730,7 @@
   /// Get the bookmark where text extraction should start, inclusive. Default is null.
   ///@return The starting bookmark.
   jni.JObject getStartBookmark() =>
-      jni.JObject.fromRef(_getStartBookmark(reference).object);
+      const jni.JObjectType().fromRef(_getStartBookmark(reference).object);
 
   static final _setStartBookmark = jniLookup<
           ffi.NativeFunction<
@@ -917,7 +927,7 @@
   /// Returns the string which will be used at the beginning of a paragraph.
   ///@return the paragraph start string
   jni.JString getParagraphStart() =>
-      jni.JString.fromRef(_getParagraphStart(reference).object);
+      const jni.JStringType().fromRef(_getParagraphStart(reference).object);
 
   static final _setParagraphStart = jniLookup<
           ffi.NativeFunction<
@@ -946,7 +956,7 @@
   /// Returns the string which will be used at the end of a paragraph.
   ///@return the paragraph end string
   jni.JString getParagraphEnd() =>
-      jni.JString.fromRef(_getParagraphEnd(reference).object);
+      const jni.JStringType().fromRef(_getParagraphEnd(reference).object);
 
   static final _setParagraphEnd = jniLookup<
           ffi.NativeFunction<
@@ -975,7 +985,7 @@
   /// Returns the string which will be used at the beginning of a page.
   ///@return the page start string
   jni.JString getPageStart() =>
-      jni.JString.fromRef(_getPageStart(reference).object);
+      const jni.JStringType().fromRef(_getPageStart(reference).object);
 
   static final _setPageStart = jniLookup<
           ffi.NativeFunction<
@@ -1004,7 +1014,7 @@
   /// Returns the string which will be used at the end of a page.
   ///@return the page end string
   jni.JString getPageEnd() =>
-      jni.JString.fromRef(_getPageEnd(reference).object);
+      const jni.JStringType().fromRef(_getPageEnd(reference).object);
 
   static final _setPageEnd = jniLookup<
           ffi.NativeFunction<
@@ -1033,7 +1043,7 @@
   /// Returns the string which will be used at the beginning of an article.
   ///@return the article start string
   jni.JString getArticleStart() =>
-      jni.JString.fromRef(_getArticleStart(reference).object);
+      const jni.JStringType().fromRef(_getArticleStart(reference).object);
 
   static final _setArticleStart = jniLookup<
           ffi.NativeFunction<
@@ -1062,7 +1072,7 @@
   /// Returns the string which will be used at the end of an article.
   ///@return the article end string
   jni.JString getArticleEnd() =>
-      jni.JString.fromRef(_getArticleEnd(reference).object);
+      const jni.JStringType().fromRef(_getArticleEnd(reference).object);
 
   static final _setArticleEnd = jniLookup<
           ffi.NativeFunction<
@@ -1181,7 +1191,7 @@
   /// This method returns a list of such regular expression Patterns.
   ///@return a list of Pattern objects.
   jni.JObject getListItemPatterns() =>
-      jni.JObject.fromRef(_getListItemPatterns(reference).object);
+      jni.JObjectType().fromRef(_getListItemPatterns(reference).object);
 
   static final _matchPattern = jniLookup<
           ffi.NativeFunction<
@@ -1205,21 +1215,24 @@
   ///@param patterns list of patterns
   ///@return matching pattern
   static jni.JObject matchPattern(jni.JString string, jni.JObject patterns) =>
-      jni.JObject.fromRef(
-          _matchPattern(string.reference, patterns.reference).object);
+      const jni.JObjectType()
+          .fromRef(_matchPattern(string.reference, patterns.reference).object);
 }
 
-class _$PDFTextStripperType extends jni.JType<PDFTextStripper> {
-  const _$PDFTextStripperType();
+class $PDFTextStripperType extends jni.JObjType<PDFTextStripper> {
+  const $PDFTextStripperType();
 
   @override
   String get signature => r"Lorg/apache/pdfbox/text/PDFTextStripper;";
+
+  @override
+  PDFTextStripper fromRef(jni.JObjectPtr ref) => PDFTextStripper.fromRef(ref);
 }
 
 extension $PDFTextStripperArray on jni.JArray<PDFTextStripper> {
   PDFTextStripper operator [](int index) {
-    return PDFTextStripper.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $PDFTextStripperType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, PDFTextStripper value) {
diff --git a/pkgs/jnigen/lib/src/bindings/common.dart b/pkgs/jnigen/lib/src/bindings/common.dart
index 61931b8..5c5a2c8 100644
--- a/pkgs/jnigen/lib/src/bindings/common.dart
+++ b/pkgs/jnigen/lib/src/bindings/common.dart
@@ -30,15 +30,25 @@
 
   static const String jthrowableType = '${jni}JThrowablePtr';
 
+  static const String jniStringType = '${jni}JString';
+  static const String jniStringTypeClass = '${jni}JString$typeClassSuffix';
+
   static const String jniObjectType = '${jni}JObject';
+  static const String jniObjectTypeClass = '${jni}JObject$typeClassSuffix';
 
   static const String jniArrayType = '${jni}JArray';
+  static const String jniArrayTypeClass = '${jni}JArray$typeClassSuffix';
 
   static const String jniCallType = '${jni}JniCallType';
 
-  static const String jniTypeType = '${jni}JType';
+  static const String jniTypeType = '${jni}JObjType';
   static const String typeClassSuffix = 'Type';
-  static const String typeClassPrefix = '_\$';
+  // TODO(#143): this is a temporary fix for the name collision.
+  static const String typeClassPrefix = '\$';
+
+  static const String instanceTypeGetter = '\$type';
+
+  static const String typeParamPrefix = '\$';
 
   static const String jniResultType = '${jni}JniResult';
 
@@ -71,9 +81,19 @@
   /// Returns the formal parameters list of the generated function.
   ///
   /// This is the signature seen by the user.
-  String getFormalArgs(Method m, SymbolResolver resolver) {
+  String getFormalArgs(ClassDecl c, Method m, SymbolResolver resolver) {
     final List<String> args = [];
-    for (var param in m.params) {
+    // Prepending the parameters with type parameters
+    if (isCtor(m)) {
+      for (final typeParam in c.allTypeParams) {
+        args.add('this.$typeParamPrefix${typeParam.name}');
+      }
+    }
+    for (final typeParam in m.typeParams) {
+      args.add(
+          '$jniTypeType<${typeParam.name}> $typeParamPrefix${typeParam.name}');
+    }
+    for (final param in m.params) {
       args.add(
           '${getDartOuterType(param.type, resolver)} ${kwRename(param.name)}');
     }
@@ -90,44 +110,154 @@
     return args.join(', ');
   }
 
+  String _dartTypeClassName(String className) {
+    return '$typeClassPrefix$className$typeClassSuffix';
+  }
+
+  String dartTypeParams(List<TypeParam> typeParams,
+      {required bool includeExtends}) {
+    if (typeParams.isEmpty) return '';
+    // TODO(#144): resolve the actual type being extended, if any.
+    final ifExtendsIncluded = includeExtends ? ' extends $jniObjectType' : '';
+    final args =
+        typeParams.map((e) => '${e.name}$ifExtendsIncluded').join(' ,');
+    return '<$args>';
+  }
+
+  String dartClassDefinition(ClassDecl decl, SymbolResolver resolver) {
+    final name = decl.finalName;
+    var superName = jniObjectType;
+    if (decl.superclass != null) {
+      superName = _dartType(decl.superclass!, resolver: resolver);
+    }
+    final typeParamsWithExtend =
+        dartTypeParams(decl.allTypeParams, includeExtends: true);
+    final ifSomeArgs =
+        decl.allTypeParams.isNotEmpty ? '(${_typeParamArgs(decl)})' : '';
+    return 'class $name$typeParamsWithExtend extends $superName {\n'
+        'late final $jniTypeType? _$instanceTypeGetter;\n'
+        '@override\n'
+        '$jniTypeType get $instanceTypeGetter => '
+        '_$instanceTypeGetter ??= type$ifSomeArgs;\n\n'
+        '${_typeParamDefs(decl)}\n'
+        '$indent$name.fromRef(\n'
+        '${_typeParamCtorArgs(decl)}'
+        '$jobjectType ref,'
+        '): super.fromRef(${dartSuperArgs(decl, resolver)}ref);\n\n';
+  }
+
   String dartSigForField(Field f,
       {bool isSetter = false, required bool isFfiSig}) {
-    final conv = isFfiSig ? getDartFfiType : getDartInnerType;
     final ref = f.modifiers.contains('static') ? '' : '$jobjectType, ';
+    final conv = isFfiSig ? getDartFfiType : getDartInnerType;
     if (isSetter) {
       return '$jthrowableType Function($ref${conv(f.type)})';
     }
     return '$jniResultType Function($ref)';
   }
 
+  String dartSuperArgs(ClassDecl decl, SymbolResolver resolver) {
+    if (decl.superclass == null ||
+        resolver.resolve((decl.superclass!.type as DeclaredType).binaryName) ==
+            null) {
+      return '';
+    }
+    return (decl.superclass!.type as DeclaredType)
+        .params
+        .map((param) => '${getDartTypeClass(param, resolver)},')
+        .join();
+  }
+
   String dartArrayExtension(ClassDecl decl) {
     final name = decl.finalName;
-    return '\nextension \$${name}Array on $jniArrayType<$name> {\n'
-        '$indent$name operator [](int index) {\n'
-        '${indent * 2}return $name.fromRef(elementAt(index, ${jni}JniCallType.objectType).object);\n'
+    final typeParamsWithExtend =
+        dartTypeParams(decl.allTypeParams, includeExtends: true);
+    final typeParams =
+        dartTypeParams(decl.allTypeParams, includeExtends: false);
+    final typeClassName = _dartTypeClassName(name);
+    return '\nextension \$${name}Array$typeParamsWithExtend on $jniArrayType<$name$typeParams> {\n'
+        '$indent$name$typeParams operator [](int index) {\n'
+        '${indent * 2}return (elementType as $typeClassName$typeParams)'
+        '.fromRef(elementAt(index, ${jni}JniCallType.objectType).object);\n'
         '$indent}\n\n'
-        '${indent}void operator []=(int index, $name value) {\n'
+        '${indent}void operator []=(int index, $name$typeParams value) {\n'
         '${indent * 2}(this as $jniArrayType<$jniObjectType>)[index] = value;\n'
         '$indent}\n'
         '}\n';
   }
 
+  String _typeParamDefs(ClassDecl decl) {
+    return decl.allTypeParams
+        .map((e) =>
+            '${indent}final $jniTypeType<${e.name}> $typeParamPrefix${e.name};\n')
+        .join();
+  }
+
+  String _typeParamCtorArgs(ClassDecl decl) {
+    return decl.allTypeParams
+        .map((e) => '${indent * 2}this.$typeParamPrefix${e.name},\n')
+        .join();
+  }
+
+  String _typeParamArgs(ClassDecl decl) {
+    return decl.allTypeParams.map((e) => '$typeParamPrefix${e.name}, ').join();
+  }
+
   String dartTypeClass(ClassDecl decl) {
     final name = decl.finalName;
     final signature = getSignature(decl.binaryName);
-    final typeClassName = '$typeClassPrefix$name$typeClassSuffix';
-    return '\nclass $typeClassName extends $jniTypeType<$name> {\n'
-        '${indent}const $typeClassName();\n\n'
+    final typeClassName = _dartTypeClassName(name);
+    final typeParamsWithExtend =
+        dartTypeParams(decl.allTypeParams, includeExtends: true);
+    final typeParams =
+        dartTypeParams(decl.allTypeParams, includeExtends: false);
+
+    return '\nclass $typeClassName$typeParamsWithExtend extends $jniTypeType<$name$typeParams> {\n'
+        '${_typeParamDefs(decl)}\n'
+        '${indent}const $typeClassName(\n'
+        '${_typeParamCtorArgs(decl)}'
+        '$indent);\n\n'
         '$indent@override\n'
-        '${indent}String get signature => r"$signature";\n'
+        '${indent}String get signature => r"$signature";\n\n'
+        '$indent@override\n'
+        '$indent$name$typeParams fromRef($jobjectType ref) => $name.fromRef(${_typeParamArgs(decl)}ref);\n'
         '}\n';
   }
 
+  String dartInitType(ClassDecl decl) {
+    final typeClassName = _dartTypeClassName(decl.finalName);
+    final args =
+        decl.allTypeParams.map((e) => '$typeParamPrefix${e.name},').join();
+    return '$instanceTypeGetter = $typeClassName($args)';
+  }
+
   String dartStaticTypeGetter(ClassDecl decl) {
-    final name = decl.finalName;
-    final typeClassName = '$typeClassPrefix$name$typeClassSuffix';
-    return '\n$indent/// The type which includes information such as the signature of this class.\n'
-        '${indent}static const $jniTypeType<$name> type = $typeClassName();\n';
+    final typeClassName = _dartTypeClassName(decl.finalName);
+    const docs =
+        '/// The type which includes information such as the signature of this class.';
+    if (decl.allTypeParams.isEmpty) {
+      return '$indent$docs\n'
+          '${indent}static const type = $typeClassName();\n\n';
+    }
+    final typeParamsWithExtend =
+        dartTypeParams(decl.allTypeParams, includeExtends: true);
+    final typeParams =
+        dartTypeParams(decl.allTypeParams, includeExtends: false);
+    final methodArgs = decl.allTypeParams
+        .map((e) =>
+            '${indent * 2}$jniTypeType<${e.name}> $typeParamPrefix${e.name},\n')
+        .join();
+    final ctorArgs = decl.allTypeParams
+        .map((e) => '${indent * 3}$typeParamPrefix${e.name},\n')
+        .join();
+    return '$indent$docs\n'
+        '${indent}static $typeClassName$typeParams type$typeParamsWithExtend(\n'
+        '$methodArgs'
+        '$indent) {\n'
+        '${indent * 2}return $typeClassName(\n'
+        '$ctorArgs'
+        '${indent * 2});\n'
+        '$indent}\n\n';
   }
 
   String dartSigForMethod(Method m, {required bool isFfiSig}) {
@@ -139,7 +269,10 @@
     return '$jniResultType Function (${argTypes.join(", ")})';
   }
 
-  String _dartType(TypeUsage t, {SymbolResolver? resolver}) {
+  String _dartType(
+    TypeUsage t, {
+    SymbolResolver? resolver,
+  }) {
     // if resolver == null, looking for inner fn type, type of fn reference
     // else looking for outer fn type, that's what user of the library sees.
     const primitives = {
@@ -168,8 +301,12 @@
         if (t.name == 'boolean' && resolver == null) return 'int';
         return primitives[(t.type as PrimitiveType).name]!;
       case Kind.typeVariable:
+        if (resolver != null) {
+          return t.name;
+        }
+        return voidPointer;
       case Kind.wildcard:
-        throw SkipException('Not supported: generics');
+        throw SkipException('Wildcards are not yet supported');
       case Kind.array:
         if (resolver != null) {
           final innerType = (t.type as ArrayType).type;
@@ -181,13 +318,139 @@
         return voidPointer;
       case Kind.declared:
         if (resolver != null) {
-          return resolver.resolve((t.type as DeclaredType).binaryName) ??
-              jniObjectType;
+          final type = t.type as DeclaredType;
+          final resolved = resolver.resolve(type.binaryName);
+          if (resolved == null) {
+            return jniObjectType;
+          }
+
+          // All type parameters of this type
+          final allTypeParams =
+              (resolver.resolveClass(type.binaryName)?.allTypeParams ?? [])
+                  .map((param) => param.name)
+                  .toList();
+
+          // The ones that are declared.
+          final paramTypeClasses =
+              type.params.map((param) => _dartType(param, resolver: resolver));
+
+          // Replacing the declared ones. They come at the end.
+          if (allTypeParams.length >= type.params.length) {
+            allTypeParams.replaceRange(
+              allTypeParams.length - type.params.length,
+              allTypeParams.length,
+              paramTypeClasses,
+            );
+          }
+
+          final args = allTypeParams.join(',');
+          final ifArgs = args.isNotEmpty ? '<$args>' : '';
+          return '$resolved$ifArgs';
         }
         return voidPointer;
     }
   }
 
+  String getDartTypeClass(
+    TypeUsage t,
+    SymbolResolver resolver,
+  ) {
+    return _getDartTypeClass(t, resolver, addConst: true).name;
+  }
+
+  _TypeClass _getDartTypeClass(
+    TypeUsage t,
+    SymbolResolver resolver, {
+    required bool addConst,
+  }) {
+    const primitives = {
+      'byte': 'JByteType',
+      'short': 'JShortType',
+      'char': 'JCharType',
+      'int': 'JIntType',
+      'long': 'JLongType',
+      'float': 'JFloatType',
+      'double': 'JDoubleType',
+      'boolean': 'JBooleanType',
+      'void': 'JVoidType', // This will never be in the generated code.
+    };
+    switch (t.kind) {
+      case Kind.primitive:
+        final ifConst = addConst ? 'const ' : '';
+        return _TypeClass(
+          '$ifConst$jni${primitives[(t.type as PrimitiveType).name]}()',
+          true,
+        );
+      case Kind.typeVariable:
+        return _TypeClass(
+          '$typeParamPrefix${(t.type as TypeVar).name}',
+          false,
+        );
+      case Kind.wildcard:
+        throw SkipException('Wildcards are not yet supported');
+      case Kind.array:
+        final innerType = (t.type as ArrayType).type;
+        final innerTypeClass = _getDartTypeClass(
+          innerType,
+          resolver,
+          addConst: false,
+        );
+        final ifConst = addConst && innerTypeClass.canBeConst ? 'const ' : '';
+        return _TypeClass(
+          '$ifConst$jniArrayTypeClass(${innerTypeClass.name})',
+          innerTypeClass.canBeConst,
+        );
+      case Kind.declared:
+        final type = (t.type as DeclaredType);
+        final resolved = resolver.resolve(type.binaryName);
+        final resolvedClass = resolver.resolveClass(type.binaryName);
+
+        // All type params of this type
+        final allTypeParams = resolvedClass?.allTypeParams
+                .map((param) => '$typeParamPrefix${param.name}')
+                .toList() ??
+            [];
+
+        // The ones that are declared.
+        final paramTypeClasses = type.params.map(
+            (param) => _getDartTypeClass(param, resolver, addConst: false));
+
+        // Replacing the declared ones. They come at the end.
+        if (allTypeParams.length >= type.params.length) {
+          allTypeParams.replaceRange(
+            allTypeParams.length - type.params.length,
+            allTypeParams.length,
+            paramTypeClasses.map((param) => param.name),
+          );
+        }
+
+        final args = allTypeParams.join(',');
+
+        final canBeConst = allTypeParams.length == paramTypeClasses.length &&
+            paramTypeClasses.every((e) => e.canBeConst);
+        final ifConst = addConst && canBeConst ? 'const ' : '';
+
+        if (resolved == null || resolved == jniObjectType) {
+          return _TypeClass('$ifConst$jniObjectTypeClass()', true);
+        } else if (resolved == jniStringType) {
+          return _TypeClass('$ifConst$jniStringTypeClass()', true);
+        } else if (resolved.contains('.')) {
+          // It is in form of jni.SomeClass which should be converted to jni.$SomeClassType
+          final dotIndex = resolved.indexOf('.');
+          final module = resolved.substring(0, dotIndex);
+          final clazz = resolved.substring(dotIndex + 1);
+          return _TypeClass(
+            '$ifConst$module.${_dartTypeClassName(clazz)}($args)',
+            canBeConst,
+          );
+        }
+        return _TypeClass(
+          '$ifConst${_dartTypeClassName(resolved)}($args)',
+          canBeConst,
+        );
+    }
+  }
+
   /// Get corresponding Dart FFI type of Java type.
   String getDartFfiType(TypeUsage t) {
     const primitives = {
@@ -204,10 +467,9 @@
     switch (t.kind) {
       case Kind.primitive:
         return ffi + primitives[(t.type as PrimitiveType).name]!;
-      case Kind.typeVariable:
       case Kind.wildcard:
-        throw SkipException(
-            'Generic type parameters are not supported', t.name);
+        throw SkipException('Wildcards are not yet supported');
+      case Kind.typeVariable:
       case Kind.array:
       case Kind.declared:
         return voidPointer;
@@ -272,11 +534,11 @@
     return '$name.$selfPointer';
   }
 
-  String toDartResult(String expr, TypeUsage type, String dartType) {
+  String toDartResult(String expr, TypeUsage type, String dartTypeClass) {
     if (isPrimitive(type)) {
       return expr;
     }
-    return '$dartType.fromRef($expr)';
+    return '$dartTypeClass.fromRef($expr)';
   }
 
   static final deleteInstruction =
@@ -490,3 +752,10 @@
   s.write(returnType);
   return s.toString();
 }
+
+class _TypeClass {
+  final String name;
+  final bool canBeConst;
+
+  _TypeClass(this.name, this.canBeConst);
+}
diff --git a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
index ba440a1..e9d58c0 100644
--- a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
@@ -28,7 +28,8 @@
   static const jniObjectType = BindingsGenerator.jniObjectType;
 
   CBasedDartBindingsGenerator(this.config);
-  Config config;
+
+  final Config config;
 
   @override
   String generateBindings(ClassDecl decl, SymbolResolver resolver) {
@@ -49,17 +50,8 @@
 
     s.write('/// from: ${decl.binaryName}\n');
     s.write(breakDocComment(decl.javadoc, depth: ''));
-    final name = decl.finalName;
 
-    var superName = jniObjectType;
-    if (decl.superclass != null) {
-      superName = resolver
-              .resolve((decl.superclass!.type as DeclaredType).binaryName) ??
-          jniObjectType;
-    }
-
-    s.write('class $name extends $superName {\n'
-        '$indent$name.fromRef($voidPointer ref) : super.fromRef(ref);\n\n');
+    s.write(dartClassDefinition(decl, resolver));
     s.write(dartStaticTypeGetter(decl));
     for (var field in decl.fields) {
       if (!field.isIncluded) {
@@ -100,6 +92,7 @@
     final ffiSig = dartSigForMethod(m, isFfiSig: true);
     final dartSig = dartSigForMethod(m, isFfiSig: false);
     final returnType = getDartOuterType(m.returnType, resolver);
+    final returnTypeClass = getDartTypeClass(m.returnType, resolver);
     final ifStaticMethod = isStaticMethod(m) ? 'static' : '';
 
     // Load corresponding C method.
@@ -119,16 +112,21 @@
       final className = c.finalName;
       final ctorFnName = name == 'ctor' ? className : '$className.$name';
       final wrapperExpr = '$sym(${actualArgs(m)})';
-      s.write('$ctorFnName(${getFormalArgs(m, resolver)}) : '
-          'super.fromRef($wrapperExpr.object);\n');
+      s.write(
+        '$ctorFnName(${getFormalArgs(c, m, resolver)}) : '
+        'super.fromRef(${dartSuperArgs(c, resolver)}$wrapperExpr.object);\n',
+      );
       return s.toString();
     }
 
     final resultGetter = getJValueAccessor(m.returnType);
     var wrapperExpr = '$sym(${actualArgs(m)}).$resultGetter';
-    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnType);
-    final params = getFormalArgs(m, resolver);
-    s.write('$indent$ifStaticMethod $returnType $name($params) '
+    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
+    final typeParamsWithExtend =
+        dartTypeParams(m.typeParams, includeExtends: true);
+    final params = getFormalArgs(c, m, resolver);
+    s.write(
+        '$indent$ifStaticMethod $returnType $name$typeParamsWithExtend($params) '
         '=> $wrapperExpr;\n');
     return s.toString();
   }
@@ -176,9 +174,10 @@
         // getter
         final self = isStaticField(f) ? '' : selfPointer;
         final outerType = getDartOuterType(f.type, resolver);
+        final outerTypeClass = getDartTypeClass(f.type, resolver);
         final resultGetter = getJValueAccessor(f.type);
         final callExpr = '$sym($self).$resultGetter';
-        final resultExpr = toDartResult(callExpr, f.type, outerType);
+        final resultExpr = toDartResult(callExpr, f.type, outerTypeClass);
         s.write('$indent$ifStatic $outerType get $name => $resultExpr;\n');
       }
     }
@@ -215,6 +214,7 @@
       '// ignore_for_file: file_names\n'
       '// ignore_for_file: no_leading_underscores_for_local_identifiers\n'
       '// ignore_for_file: non_constant_identifier_names\n'
+      '// ignore_for_file: overridden_fields\n'
       '// ignore_for_file: unnecessary_cast\n'
       '// ignore_for_file: unused_element\n'
       '// ignore_for_file: unused_import\n'
diff --git a/pkgs/jnigen/lib/src/bindings/preprocessor.dart b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
index 0a451a5..de49ade 100644
--- a/pkgs/jnigen/lib/src/bindings/preprocessor.dart
+++ b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
@@ -13,8 +13,8 @@
 abstract class ApiPreprocessor {
   static void preprocessAll(Map<String, ClassDecl> classes, Config config,
       {bool renameClasses = false}) {
-    final Map<String, int> classNameCounts = {};
-    for (var c in classes.values) {
+    final classNameCounts = <String, int>{};
+    for (final c in classes.values) {
       final className = getSimplifiedClassName(c.binaryName);
       c.uniqueName = renameConflict(classNameCounts, className);
       if (renameClasses) {
@@ -24,6 +24,11 @@
       }
       _preprocess(c, classes, config);
     }
+    // Adding type params of outer classes to the nested classes
+    final visited = <ClassDecl>{};
+    for (final c in classes.values) {
+      _traverseAddTypeParams(visited, c);
+    }
   }
 
   static void _preprocess(
@@ -35,6 +40,9 @@
       decl.isPreprocessed = true;
       return;
     }
+    if (decl.parentName != null && classes.containsKey(decl.parentName!)) {
+      decl.parent = classes[decl.parentName];
+    }
     ClassDecl? superclass;
     if (decl.superclass != null && classes.containsKey(decl.superclass?.name)) {
       superclass = classes[decl.superclass!.name]!;
@@ -89,6 +97,36 @@
     log.fine('preprocessed ${decl.binaryName}');
   }
 
+  /// Gathers all the type params from ancestors of a [ClassDecl] and store
+  /// them in [allTypeParams].
+  ///
+  /// Class A is a parent of Class B when B is nested inside A.
+  static void _traverseAddTypeParams(Set<ClassDecl> visited, ClassDecl decl) {
+    if (visited.contains(decl)) {
+      // The type params of its ancestors have already been added.
+      return;
+    }
+    final allTypeParams = <TypeParam>[];
+    if (decl.parent != null) {
+      // Adding all type params of parent's ancestors to the parent.
+      if (!visited.contains(decl.parent) && decl.parent != decl) {
+        _traverseAddTypeParams(visited, decl.parent!);
+      }
+      // Adding the type params of ancestors if the class is not static.
+      if (!decl.modifiers.contains('static')) {
+        for (final typeParam in decl.parent!.allTypeParams) {
+          if (!decl.allTypeParams.contains(typeParam)) {
+            // Add only if it's not shadowing another type param.
+            allTypeParams.add(typeParam);
+          }
+        }
+      }
+    }
+    allTypeParams.addAll(decl.typeParams);
+    decl.allTypeParams = allTypeParams;
+    visited.add(decl);
+  }
+
   static bool _isPublicOrProtected(Set<String> modifiers) =>
       modifiers.contains("public") || modifiers.contains("protected");
 
diff --git a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
index ec3699f..46f168b 100644
--- a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
@@ -33,7 +33,7 @@
   String escapeDollarSign(String s) => s.replaceAll('\$', '\\\$');
 
   PureDartBindingsGenerator(this.config);
-  Config config;
+  final Config config;
 
   @override
   String generateBindings(ClassDecl decl, SymbolResolver resolver) {
@@ -55,19 +55,13 @@
 
     s.write('/// from: ${decl.binaryName}\n');
     s.write(breakDocComment(decl.javadoc, depth: ''));
-    final name = decl.finalName;
 
-    var superName = jniObjectType;
-    if (decl.superclass != null) {
-      superName = resolver
-              .resolve((decl.superclass!.type as DeclaredType).binaryName) ??
-          jniObjectType;
-    }
     final internalName = escapeDollarSign(getInternalName(decl.binaryName));
-    s.write('class $name extends $superName {\n'
-        '  static final $classRef = $accessors.getClassOf("$internalName");\n'
-        '  $indent$name.fromRef($jobjectType ref) : super.fromRef(ref);\n'
-        '\n');
+
+    s.write(dartClassDefinition(decl, resolver));
+    s.write(
+        '${indent}static final $classRef = $accessors.getClassOf("$internalName");\n');
+
     s.write(dartStaticTypeGetter(decl));
     for (var field in decl.fields) {
       if (!field.isIncluded) continue;
@@ -106,14 +100,6 @@
   }
 
   @override
-  String toDartResult(String expr, TypeUsage type, String dartType) {
-    if (isPrimitive(type)) {
-      return expr;
-    }
-    return '$dartType.fromRef($expr)';
-  }
-
-  @override
   String actualArgs(Method m, {bool addSelf = false}) {
     return super.actualArgs(m, addSelf: addSelf);
   }
@@ -132,6 +118,7 @@
     // Different logic for constructor and method;
     // For constructor, we want return type to be new object.
     final returnType = getDartOuterType(m.returnType, resolver);
+    final returnTypeClass = getDartTypeClass(m.returnType, resolver);
     s.write('$indent/// from: ${getOriginalMethodHeader(m)}\n');
     if (!isPrimitive(m.returnType) || isCtor(m)) {
       s.write(_deleteInstruction);
@@ -142,8 +129,10 @@
           '$accessors.newObjectWithArgs($classRef, $mID, [${actualArgs(m)}]).object';
       final className = c.finalName;
       final ctorFnName = name == 'ctor' ? className : '$className.$name';
-      s.write('$ctorFnName(${getFormalArgs(m, resolver)}) : '
-          'super.fromRef($wrapperExpr);\n');
+      s.write(
+        '$ctorFnName(${getFormalArgs(c, m, resolver)}) : '
+        'super.fromRef(${dartSuperArgs(c, resolver)}$wrapperExpr);\n',
+      );
       return s.toString();
     }
 
@@ -154,12 +143,14 @@
     final selfArgument = isStatic ? classRef : selfPointer;
     final callType = getCallType(m.returnType);
     final resultGetter = getResultGetterName(m.returnType);
+    final typeParamsWithExtend =
+        dartTypeParams(m.typeParams, includeExtends: true);
     var wrapperExpr = '$accessors.call${ifStatic}MethodWithArgs'
         '($selfArgument, $mID, $callType, [${actualArgs(m)}])'
         '.$resultGetter';
-    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnType);
+    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
     s.write(
-        '$returnType $name(${getFormalArgs(m, resolver)}) => $wrapperExpr;\n');
+        '$returnType $name$typeParamsWithExtend(${getFormalArgs(c, m, resolver)}) => $wrapperExpr;\n');
     return s.toString();
   }
 
@@ -204,10 +195,11 @@
             '${toNativeArg("value", f.type, convertBooleanToInt: true)});\n');
       } else {
         final outer = getDartOuterType(f.type, resolver);
+        final typeClass = getDartTypeClass(f.type, resolver);
         final callExpr =
             '$accessors.get${ifStatic}Field($selfArgument, $fID, $callType)'
             '.$resultGetter';
-        final resultExpr = toDartResult(callExpr, f.type, outer);
+        final resultExpr = toDartResult(callExpr, f.type, typeClass);
         s.write('$outer get $name => $resultExpr;\n');
       }
     }
@@ -227,6 +219,7 @@
       '// ignore_for_file: file_names\n'
       '// ignore_for_file: no_leading_underscores_for_local_identifiers\n'
       '// ignore_for_file: non_constant_identifier_names\n'
+      '// ignore_for_file: overridden_fields\n'
       '// ignore_for_file: unnecessary_cast\n'
       '// ignore_for_file: unused_element\n'
       '// ignore_for_file: unused_field\n'
diff --git a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart b/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
index adf065b..4b158b9 100644
--- a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
+++ b/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
@@ -2,12 +2,16 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import '../elements/elements.dart';
+
 /// Resolves types referred to in method signatures etc.. and provides
 /// appropriate imports for them.
 abstract class SymbolResolver {
   /// Resolve the binary name to a String which can be used in dart code.
   String? resolve(String binaryName);
 
+  ClassDecl? resolveClass(String binaryName);
+
   /// Get all imports for types so far resolved through this resolver.
   List<String> getImportStrings();
 }
diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart
index 0c07b43..80bdbb9 100644
--- a/pkgs/jnigen/lib/src/elements/elements.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.dart
@@ -99,6 +99,14 @@
   @JsonKey(ignore: true)
   late String finalName;
 
+  /// Parent's [ClassDecl] obtained from [parentName]
+  @JsonKey(ignore: true)
+  ClassDecl? parent;
+
+  /// Type parameters including the ones from its ancestors
+  @JsonKey(ignore: true)
+  List<TypeParam> allTypeParams = const [];
+
   /// Unique name obtained by renaming conflicting names with a number.
   ///
   /// This is used by C bindings instead of fully qualified name to reduce
diff --git a/pkgs/jnigen/lib/src/writers/files_writer.dart b/pkgs/jnigen/lib/src/writers/files_writer.dart
index 4c552ae..9e78ea3 100644
--- a/pkgs/jnigen/lib/src/writers/files_writer.dart
+++ b/pkgs/jnigen/lib/src/writers/files_writer.dart
@@ -23,16 +23,20 @@
 /// Resolver for file-per-package mapping, in which the java package hierarchy
 /// is mirrored.
 class FilePathResolver implements SymbolResolver {
-  FilePathResolver(
-    this.importMap,
-    this.currentClass,
-    this.inputClassNames,
-  );
+  FilePathResolver({
+    this.classes = const {},
+    this.importMap = const {},
+    required this.currentClass,
+    this.inputClassNames = const {},
+  });
 
   static const Map<String, String> predefined = {
     'java.lang.String': 'jni.JString',
   };
 
+  /// A map of all classes by their names
+  final Map<String, ClassDecl> classes;
+
   /// Class corresponding to currently writing file.
   final String currentClass;
 
@@ -158,6 +162,11 @@
   List<String> getImportStrings() {
     return importStrings;
   }
+
+  @override
+  ClassDecl? resolveClass(String binaryName) {
+    return classes[binaryName];
+  }
 }
 
 /// Writer which executes custom callback on passed class elements.
@@ -235,9 +244,10 @@
       final dartFile = await File.fromUri(dartFileUri).create(recursive: true);
       log.fine('$fileClassName -> ${dartFile.path}');
       final resolver = FilePathResolver(
-        config.importMap ?? const {},
-        fileClassName,
-        classNames,
+        classes: classesByName,
+        importMap: config.importMap ?? const {},
+        currentClass: fileClassName,
+        inputClassNames: classNames,
       );
 
       final classesInFile = files[fileClassName]!;
diff --git a/pkgs/jnigen/lib/src/writers/single_file_writer.dart b/pkgs/jnigen/lib/src/writers/single_file_writer.dart
index 81cc036..ee6875f 100644
--- a/pkgs/jnigen/lib/src/writers/single_file_writer.dart
+++ b/pkgs/jnigen/lib/src/writers/single_file_writer.dart
@@ -26,6 +26,11 @@
     if (predefined.containsKey(binaryName)) return predefined[binaryName];
     return inputClasses[binaryName]?.finalName;
   }
+
+  @override
+  ClassDecl? resolveClass(String binaryName) {
+    return inputClasses[binaryName];
+  }
 }
 
 class SingleFileWriter extends BindingsWriter {
diff --git a/pkgs/jnigen/test/bindings_test.dart b/pkgs/jnigen/test/bindings_test.dart
index bb6ce11..cb0aa97 100644
--- a/pkgs/jnigen/test/bindings_test.dart
+++ b/pkgs/jnigen/test/bindings_test.dart
@@ -39,6 +39,12 @@
       'javac',
       [
         join(group, 'simple_package', 'Example.java'),
+        join(group, 'generics', 'MyMap.java'),
+        join(group, 'generics', 'MyStack.java'),
+        join(group, 'generics', 'GrandParent.java'),
+        join(group, 'generics', 'StringStack.java'),
+        join(group, 'generics', 'StringValuedMap.java'),
+        join(group, 'generics', 'StringKeyedMap.java'),
         join(group, 'pkg2', 'C2.java'),
         join(group, 'pkg2', 'Example.java'),
       ],
@@ -149,4 +155,122 @@
   test('exceptions', () {
     expect(() => Example.throwException(), throwsException);
   });
+  group('generics', () {
+    test('MyStack<T>', () {
+      using((arena) {
+        final stack = MyStack(JString.type)..deletedIn(arena);
+        stack.push('Hello'.toJString()..deletedIn(arena));
+        stack.push('World'.toJString()..deletedIn(arena));
+        expect(stack.pop().toDartString(deleteOriginal: true), 'World');
+        expect(stack.pop().toDartString(deleteOriginal: true), 'Hello');
+      });
+    });
+    test('MyMap<K, V>', () {
+      using((arena) {
+        final map = MyMap(JString.type, Example.type)..deletedIn(arena);
+        final helloExample = Example.ctor1(1)..deletedIn(arena);
+        final worldExample = Example.ctor1(2)..deletedIn(arena);
+        map.put('Hello'.toJString()..deletedIn(arena), helloExample);
+        map.put('World'.toJString()..deletedIn(arena), worldExample);
+        expect(
+          (map.get0('Hello'.toJString()..deletedIn(arena))..deletedIn(arena))
+              .getInternal(),
+          1,
+        );
+        expect(
+          (map.get0('World'.toJString()..deletedIn(arena))..deletedIn(arena))
+              .getInternal(),
+          2,
+        );
+        expect(
+          ((map.entryStack()..deletedIn(arena)).pop()..deletedIn(arena))
+              .key
+              .toDartString(deleteOriginal: true),
+          anyOf('Hello', 'World'),
+        );
+      });
+    });
+    group('classes extending generics', () {
+      test('StringStack', () {
+        using((arena) {
+          final stringStack = StringStack()..deletedIn(arena);
+          stringStack.push('Hello'.toJString()..deletedIn(arena));
+          expect(stringStack.pop().toDartString(deleteOriginal: true), 'Hello');
+        });
+      });
+      test('StringKeyedMap', () {
+        using((arena) {
+          final map = StringKeyedMap(Example.type)..deletedIn(arena);
+          final example = Example()..deletedIn(arena);
+          map.put('Hello'.toJString()..deletedIn(arena), example);
+          expect(
+            (map.get0('Hello'.toJString()..deletedIn(arena))..deletedIn(arena))
+                .getInternal(),
+            0,
+          );
+        });
+      });
+      test('StringValuedMap', () {
+        using((arena) {
+          final map = StringValuedMap(Example.type)..deletedIn(arena);
+          final example = Example()..deletedIn(arena);
+          map.put(example, 'Hello'.toJString()..deletedIn(arena));
+          expect(
+            map.get0(example).toDartString(deleteOriginal: true),
+            'Hello',
+          );
+        });
+      });
+    });
+    test('nested generics', () {
+      using((arena) {
+        final grandParent =
+            GrandParent(JString.type, "!".toJString()..deletedIn(arena))
+              ..deletedIn(arena);
+        expect(
+          grandParent.value.toDartString(deleteOriginal: true),
+          "!",
+        );
+
+        final strStaticParent = GrandParent.stringStaticParent()
+          ..deletedIn(arena);
+        expect(
+          strStaticParent.value.toDartString(deleteOriginal: true),
+          "Hello",
+        );
+
+        final exampleStaticParent = GrandParent.varStaticParent(
+            Example.type, Example()..deletedIn(arena))
+          ..deletedIn(arena);
+        expect(
+          (exampleStaticParent.value..deletedIn(arena)).getInternal(),
+          0,
+        );
+
+        final strParent = grandParent.stringParent()..deletedIn(arena);
+        expect(
+          strParent.parentValue.toDartString(deleteOriginal: true),
+          "!",
+        );
+        expect(
+          strParent.value.toDartString(deleteOriginal: true),
+          "Hello",
+        );
+
+        final exampleParent = grandParent.varParent(
+            Example.type, Example()..deletedIn(arena))
+          ..deletedIn(arena);
+        expect(
+          exampleParent.parentValue.toDartString(deleteOriginal: true),
+          "!",
+        );
+        expect(
+          (exampleParent.value..deletedIn(arena)).getInternal(),
+          0,
+        );
+        // TODO(#139): test constructing Child, currently does not work due
+        // to a problem with C-bindings.
+      });
+    });
+  });
 }
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
index eac4b95..32377ce 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
@@ -24,6 +24,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_field
@@ -57,12 +58,19 @@
 /// instances.
 ///@author Tatu Saloranta
 class JsonFactory extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonFactory.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef =
       jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonFactory");
-  JsonFactory.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonFactory> type = _$JsonFactoryType();
+  static const type = $JsonFactoryType();
 
   /// from: static public final java.lang.String FORMAT_NAME_JSON
   ///
@@ -112,7 +120,7 @@
   /// from: static public final com.fasterxml.jackson.core.SerializableString DEFAULT_ROOT_VALUE_SEPARATOR
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JObject get DEFAULT_ROOT_VALUE_SEPARATOR =>
-      jni.JObject.fromRef(jniAccessors
+      const jni.JObjectType().fromRef(jniAccessors
           .getStaticField(_classRef, _id_DEFAULT_ROOT_VALUE_SEPARATOR,
               jni.JniCallType.objectType)
           .object);
@@ -196,8 +204,9 @@
   /// with settings of this factory.
   ///@return Builder instance to use
   ///@since 2.10
-  jni.JObject rebuild() => jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_rebuild, jni.JniCallType.objectType, []).object);
+  jni.JObject rebuild() =>
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_rebuild, jni.JniCallType.objectType, []).object);
 
   static final _id_builder = jniAccessors.getStaticMethodIDOf(
       _classRef, "builder", "()Lcom/fasterxml/jackson/core/TSFBuilder;");
@@ -214,7 +223,7 @@
   /// will be fixed in 3.0.
   ///@return Builder instance to use
   static jni.JObject builder() =>
-      jni.JObject.fromRef(jniAccessors.callStaticMethodWithArgs(
+      jni.JObjectType().fromRef(jniAccessors.callStaticMethodWithArgs(
           _classRef, _id_builder, jni.JniCallType.objectType, []).object);
 
   static final _id_copy = jniAccessors.getMethodIDOf(
@@ -235,8 +244,9 @@
   /// set codec after making the copy.
   ///@return Copy of this factory instance
   ///@since 2.1
-  JsonFactory copy() => JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_copy, jni.JniCallType.objectType, []).object);
+  JsonFactory copy() =>
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_copy, jni.JniCallType.objectType, []).object);
 
   static final _id_readResolve = jniAccessors.getMethodIDOf(
       _classRef, "readResolve", "()Ljava/lang/Object;");
@@ -251,7 +261,7 @@
   /// Note: must be overridden by sub-classes as well.
   ///@return Newly constructed instance
   jni.JObject readResolve() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_readResolve, jni.JniCallType.objectType, []).object);
 
   static final _id_requiresPropertyOrdering =
@@ -335,7 +345,7 @@
   /// from: public java.lang.Class<? extends com.fasterxml.jackson.core.FormatFeature> getFormatReadFeatureType()
   /// The returned object must be deleted after use, by calling the `delete` method.
   jni.JObject getFormatReadFeatureType() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getFormatReadFeatureType, jni.JniCallType.objectType, []).object);
 
   static final _id_getFormatWriteFeatureType = jniAccessors.getMethodIDOf(
@@ -344,7 +354,7 @@
   /// from: public java.lang.Class<? extends com.fasterxml.jackson.core.FormatFeature> getFormatWriteFeatureType()
   /// The returned object must be deleted after use, by calling the `delete` method.
   jni.JObject getFormatWriteFeatureType() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_getFormatWriteFeatureType,
           jni.JniCallType.objectType, []).object);
@@ -382,7 +392,7 @@
   /// implementation will return null for all sub-classes
   ///@return Name of the format handled by parsers, generators this factory creates
   jni.JString getFormatName() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_getFormatName, jni.JniCallType.objectType, []).object);
 
   static final _id_hasFormat = jniAccessors.getMethodIDOf(
@@ -393,7 +403,7 @@
   /// from: public com.fasterxml.jackson.core.format.MatchStrength hasFormat(com.fasterxml.jackson.core.format.InputAccessor acc)
   /// The returned object must be deleted after use, by calling the `delete` method.
   jni.JObject hasFormat(jni.JObject acc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_hasFormat, jni.JniCallType.objectType, [acc.reference]).object);
 
   static final _id_requiresCustomCodec =
@@ -421,7 +431,7 @@
   /// from: protected com.fasterxml.jackson.core.format.MatchStrength hasJSONFormat(com.fasterxml.jackson.core.format.InputAccessor acc)
   /// The returned object must be deleted after use, by calling the `delete` method.
   jni.JObject hasJSONFormat(jni.JObject acc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_hasJSONFormat,
           jni.JniCallType.objectType,
@@ -432,8 +442,9 @@
 
   /// from: public com.fasterxml.jackson.core.Version version()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JObject version() => jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_version, jni.JniCallType.objectType, []).object);
+  jni.JObject version() =>
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_version, jni.JniCallType.objectType, []).object);
 
   static final _id_configure = jniAccessors.getMethodIDOf(
       _classRef,
@@ -450,7 +461,7 @@
   ///@return This factory instance (to allow call chaining)
   ///@deprecated since 2.10 use JsonFactoryBuilder\#configure(JsonFactory.Feature, boolean) instead
   JsonFactory configure(JsonFactory_Feature f, bool state) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_configure,
           jni.JniCallType.objectType,
@@ -468,8 +479,11 @@
   ///@return This factory instance (to allow call chaining)
   ///@deprecated since 2.10 use JsonFactoryBuilder\#configure(JsonFactory.Feature, boolean) instead
   JsonFactory enable(JsonFactory_Feature f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference, _id_enable,
-          jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_enable,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_disable = jniAccessors.getMethodIDOf(_classRef, "disable",
       "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
@@ -483,8 +497,11 @@
   ///@return This factory instance (to allow call chaining)
   ///@deprecated since 2.10 use JsonFactoryBuilder\#configure(JsonFactory.Feature, boolean) instead
   JsonFactory disable(JsonFactory_Feature f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_disable, jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_disable,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_isEnabled = jniAccessors.getMethodIDOf(_classRef,
       "isEnabled", "(Lcom/fasterxml/jackson/core/JsonFactory\$Feature;)Z");
@@ -542,7 +559,7 @@
   ///@param state Whether to enable or disable the feature
   ///@return This factory instance (to allow call chaining)
   JsonFactory configure1(jsonparser_.JsonParser_Feature f, bool state) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_configure1,
           jni.JniCallType.objectType,
@@ -559,8 +576,11 @@
   ///@param f Feature to enable
   ///@return This factory instance (to allow call chaining)
   JsonFactory enable1(jsonparser_.JsonParser_Feature f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_enable1, jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_enable1,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_disable1 = jniAccessors.getMethodIDOf(_classRef, "disable",
       "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
@@ -573,8 +593,11 @@
   ///@param f Feature to disable
   ///@return This factory instance (to allow call chaining)
   JsonFactory disable1(jsonparser_.JsonParser_Feature f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_disable1, jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_disable1,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_isEnabled1 = jniAccessors.getMethodIDOf(_classRef,
       "isEnabled", "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Z");
@@ -610,7 +633,7 @@
   /// there is no default decorator).
   ///@return InputDecorator configured, if any
   jni.JObject getInputDecorator() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getInputDecorator, jni.JniCallType.objectType, []).object);
 
   static final _id_setInputDecorator = jniAccessors.getMethodIDOf(
@@ -626,7 +649,7 @@
   ///@return This factory instance (to allow call chaining)
   ///@deprecated Since 2.10 use JsonFactoryBuilder\#inputDecorator(InputDecorator) instead
   JsonFactory setInputDecorator(jni.JObject d) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_setInputDecorator,
           jni.JniCallType.objectType,
@@ -646,7 +669,7 @@
   ///@param state Whether to enable or disable the feature
   ///@return This factory instance (to allow call chaining)
   JsonFactory configure2(jni.JObject f, bool state) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_configure2,
           jni.JniCallType.objectType,
@@ -663,8 +686,11 @@
   ///@param f Feature to enable
   ///@return This factory instance (to allow call chaining)
   JsonFactory enable2(jni.JObject f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_enable2, jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_enable2,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_disable2 = jniAccessors.getMethodIDOf(_classRef, "disable",
       "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;)Lcom/fasterxml/jackson/core/JsonFactory;");
@@ -677,8 +703,11 @@
   ///@param f Feature to disable
   ///@return This factory instance (to allow call chaining)
   JsonFactory disable2(jni.JObject f) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_disable2, jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_disable2,
+          jni.JniCallType.objectType,
+          [f.reference]).object);
 
   static final _id_isEnabled3 = jniAccessors.getMethodIDOf(_classRef,
       "isEnabled", "(Lcom/fasterxml/jackson/core/JsonGenerator\$Feature;)Z");
@@ -715,7 +744,7 @@
   /// it creates.
   ///@return Configured {@code CharacterEscapes}, if any; {@code null} if none
   jni.JObject getCharacterEscapes() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getCharacterEscapes, jni.JniCallType.objectType, []).object);
 
   static final _id_setCharacterEscapes = jniAccessors.getMethodIDOf(
@@ -731,7 +760,7 @@
   ///@param esc CharaterEscapes to set (or {@code null} for "none")
   ///@return This factory instance (to allow call chaining)
   JsonFactory setCharacterEscapes(jni.JObject esc) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_setCharacterEscapes,
           jni.JniCallType.objectType,
@@ -750,7 +779,7 @@
   ///@return OutputDecorator configured for generators factory creates, if any;
   ///    {@code null} if none.
   jni.JObject getOutputDecorator() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getOutputDecorator, jni.JniCallType.objectType, []).object);
 
   static final _id_setOutputDecorator = jniAccessors.getMethodIDOf(
@@ -766,7 +795,7 @@
   ///@param d Output decorator to use, if any
   ///@deprecated Since 2.10 use JsonFactoryBuilder\#outputDecorator(OutputDecorator) instead
   JsonFactory setOutputDecorator(jni.JObject d) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_setOutputDecorator,
           jni.JniCallType.objectType,
@@ -786,7 +815,7 @@
   ///   automatically added
   ///@return This factory instance (to allow call chaining)
   JsonFactory setRootValueSeparator(jni.JString sep) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_setRootValueSeparator,
           jni.JniCallType.objectType,
@@ -800,7 +829,7 @@
   ///
   /// @return Root value separator configured, if any
   jni.JString getRootValueSeparator() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getRootValueSeparator, jni.JniCallType.objectType, []).object);
 
   static final _id_setCodec = jniAccessors.getMethodIDOf(_classRef, "setCodec",
@@ -817,16 +846,20 @@
   ///@param oc Codec to use
   ///@return This factory instance (to allow call chaining)
   JsonFactory setCodec(jni.JObject oc) =>
-      JsonFactory.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_setCodec, jni.JniCallType.objectType, [oc.reference]).object);
+      const $JsonFactoryType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_setCodec,
+          jni.JniCallType.objectType,
+          [oc.reference]).object);
 
   static final _id_getCodec = jniAccessors.getMethodIDOf(
       _classRef, "getCodec", "()Lcom/fasterxml/jackson/core/ObjectCodec;");
 
   /// from: public com.fasterxml.jackson.core.ObjectCodec getCodec()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JObject getCodec() => jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_getCodec, jni.JniCallType.objectType, []).object);
+  jni.JObject getCodec() =>
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_getCodec, jni.JniCallType.objectType, []).object);
 
   static final _id_createParser = jniAccessors.getMethodIDOf(
       _classRef,
@@ -853,8 +886,9 @@
   ///@param f File that contains JSON content to parse
   ///@since 2.1
   jsonparser_.JsonParser createParser(jni.JObject f) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_createParser, jni.JniCallType.objectType, [f.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser,
+              jni.JniCallType.objectType, [f.reference]).object);
 
   static final _id_createParser1 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -879,11 +913,9 @@
   ///@param url URL pointing to resource that contains JSON content to parse
   ///@since 2.1
   jsonparser_.JsonParser createParser1(jni.JObject url) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser1,
-          jni.JniCallType.objectType,
-          [url.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser1,
+              jni.JniCallType.objectType, [url.reference]).object);
 
   static final _id_createParser2 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -911,11 +943,9 @@
   ///@param in InputStream to use for reading JSON content to parse
   ///@since 2.1
   jsonparser_.JsonParser createParser2(jni.JObject in0) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser2,
-          jni.JniCallType.objectType,
-          [in0.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser2,
+              jni.JniCallType.objectType, [in0.reference]).object);
 
   static final _id_createParser3 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -936,8 +966,9 @@
   ///@param r Reader to use for reading JSON content to parse
   ///@since 2.1
   jsonparser_.JsonParser createParser3(jni.JObject r) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_createParser3, jni.JniCallType.objectType, [r.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser3,
+              jni.JniCallType.objectType, [r.reference]).object);
 
   static final _id_createParser4 = jniAccessors.getMethodIDOf(
       _classRef, "createParser", "([B)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -949,11 +980,9 @@
   /// the contents of given byte array.
   ///@since 2.1
   jsonparser_.JsonParser createParser4(jni.JArray<jni.JByte> data) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser4,
-          jni.JniCallType.objectType,
-          [data.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser4,
+              jni.JniCallType.objectType, [data.reference]).object);
 
   static final _id_createParser5 = jniAccessors.getMethodIDOf(_classRef,
       "createParser", "([BII)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -969,11 +998,12 @@
   ///@since 2.1
   jsonparser_.JsonParser createParser5(
           jni.JArray<jni.JByte> data, int offset, int len) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser5,
-          jni.JniCallType.objectType,
-          [data.reference, offset, len]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(
+              reference,
+              _id_createParser5,
+              jni.JniCallType.objectType,
+              [data.reference, offset, len]).object);
 
   static final _id_createParser6 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -987,11 +1017,9 @@
   /// contents of given String.
   ///@since 2.1
   jsonparser_.JsonParser createParser6(jni.JString content) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser6,
-          jni.JniCallType.objectType,
-          [content.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser6,
+              jni.JniCallType.objectType, [content.reference]).object);
 
   static final _id_createParser7 = jniAccessors.getMethodIDOf(
       _classRef, "createParser", "([C)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -1003,11 +1031,9 @@
   /// contents of given char array.
   ///@since 2.4
   jsonparser_.JsonParser createParser7(jni.JArray<jni.JChar> content) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser7,
-          jni.JniCallType.objectType,
-          [content.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser7,
+              jni.JniCallType.objectType, [content.reference]).object);
 
   static final _id_createParser8 = jniAccessors.getMethodIDOf(_classRef,
       "createParser", "([CII)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -1019,11 +1045,12 @@
   ///@since 2.4
   jsonparser_.JsonParser createParser8(
           jni.JArray<jni.JChar> content, int offset, int len) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser8,
-          jni.JniCallType.objectType,
-          [content.reference, offset, len]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(
+              reference,
+              _id_createParser8,
+              jni.JniCallType.objectType,
+              [content.reference, offset, len]).object);
 
   static final _id_createParser9 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1040,11 +1067,9 @@
   /// will throw UnsupportedOperationException
   ///@since 2.8
   jsonparser_.JsonParser createParser9(jni.JObject in0) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createParser9,
-          jni.JniCallType.objectType,
-          [in0.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createParser9,
+              jni.JniCallType.objectType, [in0.reference]).object);
 
   static final _id_createNonBlockingByteArrayParser =
       jniAccessors.getMethodIDOf(_classRef, "createNonBlockingByteArrayParser",
@@ -1067,10 +1092,9 @@
   /// at this point.
   ///@since 2.9
   jsonparser_.JsonParser createNonBlockingByteArrayParser() =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createNonBlockingByteArrayParser,
-          jni.JniCallType.objectType, []).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createNonBlockingByteArrayParser,
+              jni.JniCallType.objectType, []).object);
 
   static final _id_createGenerator = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1099,7 +1123,7 @@
   ///@param enc Character encoding to use
   ///@since 2.1
   jni.JObject createGenerator(jni.JObject out, jni.JObject enc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator,
           jni.JniCallType.objectType,
@@ -1119,7 +1143,7 @@
   /// Note: there are formats that use fixed encoding (like most binary data formats).
   ///@since 2.1
   jni.JObject createGenerator1(jni.JObject out) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator1,
           jni.JniCallType.objectType,
@@ -1145,7 +1169,7 @@
   ///@since 2.1
   ///@param w Writer to use for writing JSON content
   jni.JObject createGenerator2(jni.JObject w) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator2,
           jni.JniCallType.objectType,
@@ -1172,7 +1196,7 @@
   ///@param enc Character encoding to use
   ///@since 2.1
   jni.JObject createGenerator3(jni.JObject f, jni.JObject enc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator3,
           jni.JniCallType.objectType,
@@ -1190,7 +1214,7 @@
   /// DataOutput instance.
   ///@since 2.8
   jni.JObject createGenerator4(jni.JObject out, jni.JObject enc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator4,
           jni.JniCallType.objectType,
@@ -1210,7 +1234,7 @@
   /// Note: there are formats that use fixed encoding (like most binary data formats).
   ///@since 2.8
   jni.JObject createGenerator5(jni.JObject out) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createGenerator5,
           jni.JniCallType.objectType,
@@ -1243,11 +1267,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(File) instead.
   jsonparser_.JsonParser createJsonParser(jni.JObject f) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser,
-          jni.JniCallType.objectType,
-          [f.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser,
+              jni.JniCallType.objectType, [f.reference]).object);
 
   static final _id_createJsonParser1 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1275,11 +1297,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(URL) instead.
   jsonparser_.JsonParser createJsonParser1(jni.JObject url) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser1,
-          jni.JniCallType.objectType,
-          [url.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser1,
+              jni.JniCallType.objectType, [url.reference]).object);
 
   static final _id_createJsonParser2 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1310,11 +1330,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(InputStream) instead.
   jsonparser_.JsonParser createJsonParser2(jni.JObject in0) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser2,
-          jni.JniCallType.objectType,
-          [in0.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser2,
+              jni.JniCallType.objectType, [in0.reference]).object);
 
   static final _id_createJsonParser3 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1338,11 +1356,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(Reader) instead.
   jsonparser_.JsonParser createJsonParser3(jni.JObject r) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser3,
-          jni.JniCallType.objectType,
-          [r.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser3,
+              jni.JniCallType.objectType, [r.reference]).object);
 
   static final _id_createJsonParser4 = jniAccessors.getMethodIDOf(_classRef,
       "createJsonParser", "([B)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -1357,11 +1373,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(byte[]) instead.
   jsonparser_.JsonParser createJsonParser4(jni.JArray<jni.JByte> data) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser4,
-          jni.JniCallType.objectType,
-          [data.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser4,
+              jni.JniCallType.objectType, [data.reference]).object);
 
   static final _id_createJsonParser5 = jniAccessors.getMethodIDOf(_classRef,
       "createJsonParser", "([BII)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -1380,11 +1394,12 @@
   ///@deprecated Since 2.2, use \#createParser(byte[],int,int) instead.
   jsonparser_.JsonParser createJsonParser5(
           jni.JArray<jni.JByte> data, int offset, int len) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser5,
-          jni.JniCallType.objectType,
-          [data.reference, offset, len]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(
+              reference,
+              _id_createJsonParser5,
+              jni.JniCallType.objectType,
+              [data.reference, offset, len]).object);
 
   static final _id_createJsonParser6 = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1402,11 +1417,9 @@
   ///@throws JsonParseException if parser initialization fails due to content decoding problem
   ///@deprecated Since 2.2, use \#createParser(String) instead.
   jsonparser_.JsonParser createJsonParser6(jni.JString content) =>
-      jsonparser_.JsonParser.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_createJsonParser6,
-          jni.JniCallType.objectType,
-          [content.reference]).object);
+      const jsonparser_.$JsonParserType().fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_createJsonParser6,
+              jni.JniCallType.objectType, [content.reference]).object);
 
   static final _id_createJsonGenerator = jniAccessors.getMethodIDOf(
       _classRef,
@@ -1437,7 +1450,7 @@
   ///@throws IOException if parser initialization fails due to I/O (write) problem
   ///@deprecated Since 2.2, use \#createGenerator(OutputStream, JsonEncoding) instead.
   jni.JObject createJsonGenerator(jni.JObject out, jni.JObject enc) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createJsonGenerator,
           jni.JniCallType.objectType,
@@ -1465,7 +1478,7 @@
   ///@throws IOException if parser initialization fails due to I/O (write) problem
   ///@deprecated Since 2.2, use \#createGenerator(Writer) instead.
   jni.JObject createJsonGenerator1(jni.JObject out) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createJsonGenerator1,
           jni.JniCallType.objectType,
@@ -1488,24 +1501,27 @@
   ///@throws IOException if parser initialization fails due to I/O (write) problem
   ///@deprecated Since 2.2, use \#createGenerator(OutputStream) instead.
   jni.JObject createJsonGenerator2(jni.JObject out) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_createJsonGenerator2,
           jni.JniCallType.objectType,
           [out.reference]).object);
 }
 
-class _$JsonFactoryType extends jni.JType<JsonFactory> {
-  const _$JsonFactoryType();
+class $JsonFactoryType extends jni.JObjType<JsonFactory> {
+  const $JsonFactoryType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonFactory;";
+
+  @override
+  JsonFactory fromRef(jni.JObjectPtr ref) => JsonFactory.fromRef(ref);
 }
 
 extension $JsonFactoryArray on jni.JArray<JsonFactory> {
   JsonFactory operator [](int index) {
-    return JsonFactory.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonFactoryType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonFactory value) {
@@ -1518,20 +1534,27 @@
 /// Enumeration that defines all on/off features that can only be
 /// changed for JsonFactory.
 class JsonFactory_Feature extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonFactory_Feature.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef = jniAccessors
       .getClassOf("com/fasterxml/jackson/core/JsonFactory\$Feature");
-  JsonFactory_Feature.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonFactory_Feature> type =
-      _$JsonFactory_FeatureType();
+  static const type = $JsonFactory_FeatureType();
+
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
       "values", "()[Lcom/fasterxml/jackson/core/JsonFactory\$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonFactory.Feature[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<JsonFactory_Feature> values() =>
-      jni.JArray<JsonFactory_Feature>.fromRef(jniAccessors
+      const jni.JArrayType($JsonFactory_FeatureType()).fromRef(jniAccessors
           .callStaticMethodWithArgs(
               _classRef, _id_values, jni.JniCallType.objectType, []).object);
 
@@ -1543,11 +1566,9 @@
   /// from: static public com.fasterxml.jackson.core.JsonFactory.Feature valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
   static JsonFactory_Feature valueOf(jni.JString name) =>
-      JsonFactory_Feature.fromRef(jniAccessors.callStaticMethodWithArgs(
-          _classRef,
-          _id_valueOf,
-          jni.JniCallType.objectType,
-          [name.reference]).object);
+      const $JsonFactory_FeatureType().fromRef(jniAccessors
+          .callStaticMethodWithArgs(_classRef, _id_valueOf,
+              jni.JniCallType.objectType, [name.reference]).object);
 
   static final _id_collectDefaults =
       jniAccessors.getStaticMethodIDOf(_classRef, "collectDefaults", "()I");
@@ -1582,17 +1603,21 @@
       reference, _id_getMask, jni.JniCallType.intType, []).integer;
 }
 
-class _$JsonFactory_FeatureType extends jni.JType<JsonFactory_Feature> {
-  const _$JsonFactory_FeatureType();
+class $JsonFactory_FeatureType extends jni.JObjType<JsonFactory_Feature> {
+  const $JsonFactory_FeatureType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonFactory$Feature;";
+
+  @override
+  JsonFactory_Feature fromRef(jni.JObjectPtr ref) =>
+      JsonFactory_Feature.fromRef(ref);
 }
 
 extension $JsonFactory_FeatureArray on jni.JArray<JsonFactory_Feature> {
   JsonFactory_Feature operator [](int index) {
-    return JsonFactory_Feature.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonFactory_FeatureType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonFactory_Feature value) {
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
index 9495814..4d049fb 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
@@ -24,6 +24,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_field
@@ -44,12 +45,20 @@
 /// a JsonFactory instance.
 ///@author Tatu Saloranta
 class JsonParser extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonParser.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef =
       jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonParser");
-  JsonParser.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonParser> type = _$JsonParserType();
+  static const type = $JsonParserType();
+
   static final _id_DEFAULT_READ_CAPABILITIES = jniAccessors.getStaticFieldIDOf(
       _classRef,
       "DEFAULT_READ_CAPABILITIES",
@@ -63,7 +72,7 @@
   /// set needs to be passed).
   ///@since 2.12
   static jni.JObject get DEFAULT_READ_CAPABILITIES =>
-      jni.JObject.fromRef(jniAccessors
+      jni.JObjectType().fromRef(jniAccessors
           .getStaticField(_classRef, _id_DEFAULT_READ_CAPABILITIES,
               jni.JniCallType.objectType)
           .object);
@@ -96,8 +105,9 @@
   /// parser, if any. Codec is used by \#readValueAs(Class)
   /// method (and its variants).
   ///@return Codec assigned to this parser, if any; {@code null} if none
-  jni.JObject getCodec() => jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_getCodec, jni.JniCallType.objectType, []).object);
+  jni.JObject getCodec() =>
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_getCodec, jni.JniCallType.objectType, []).object);
 
   static final _id_setCodec = jniAccessors.getMethodIDOf(
       _classRef, "setCodec", "(Lcom/fasterxml/jackson/core/ObjectCodec;)V");
@@ -132,7 +142,7 @@
   /// "last effort", i.e. only used if no other mechanism is applicable.
   ///@return Input source this parser was configured with
   jni.JObject getInputSource() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getInputSource, jni.JniCallType.objectType, []).object);
 
   static final _id_setRequestPayloadOnError = jniAccessors.getMethodIDOf(
@@ -210,7 +220,7 @@
   ///@return Schema in use by this parser, if any; {@code null} if none
   ///@since 2.1
   jni.JObject getSchema() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_getSchema, jni.JniCallType.objectType, []).object);
 
   static final _id_canUseSchema = jniAccessors.getMethodIDOf(_classRef,
@@ -277,7 +287,7 @@
   ///@return Input feeder to use with non-blocking (async) parsing
   ///@since 2.9
   jni.JObject getNonBlockingInputFeeder() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_getNonBlockingInputFeeder,
           jni.JniCallType.objectType, []).object);
@@ -295,7 +305,7 @@
   ///@return Set of read capabilities for content to read via this parser
   ///@since 2.12
   jni.JObject getReadCapabilities() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getReadCapabilities, jni.JniCallType.objectType, []).object);
 
   static final _id_version = jniAccessors.getMethodIDOf(
@@ -308,8 +318,9 @@
   /// Left for sub-classes to implement.
   ///@return Version of this generator (derived from version declared for
   ///   {@code jackson-core} jar that contains the class
-  jni.JObject version() => jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_version, jni.JniCallType.objectType, []).object);
+  jni.JObject version() =>
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_version, jni.JniCallType.objectType, []).object);
 
   static final _id_close =
       jniAccessors.getMethodIDOf(_classRef, "close", "()V");
@@ -364,7 +375,7 @@
   /// input, if so desired.
   ///@return Stream input context (JsonStreamContext) associated with this parser
   jni.JObject getParsingContext() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getParsingContext, jni.JniCallType.objectType, []).object);
 
   static final _id_currentLocation = jniAccessors.getMethodIDOf(_classRef,
@@ -386,7 +397,7 @@
   ///@return Location of the last processed input unit (byte or character)
   ///@since 2.13
   jni.JObject currentLocation() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_currentLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_currentTokenLocation = jniAccessors.getMethodIDOf(_classRef,
@@ -408,7 +419,7 @@
   ///@return Starting location of the token parser currently points to
   ///@since 2.13 (will eventually replace \#getTokenLocation)
   jni.JObject currentTokenLocation() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_currentTokenLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_getCurrentLocation = jniAccessors.getMethodIDOf(_classRef,
@@ -421,7 +432,7 @@
   /// Jackson 2.x versions (and removed from Jackson 3.0).
   ///@return Location of the last processed input unit (byte or character)
   jni.JObject getCurrentLocation() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getCurrentLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_getTokenLocation = jniAccessors.getMethodIDOf(_classRef,
@@ -434,7 +445,7 @@
   /// Jackson 2.x versions (and removed from Jackson 3.0).
   ///@return Starting location of the token parser currently points to
   jni.JObject getTokenLocation() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getTokenLocation, jni.JniCallType.objectType, []).object);
 
   static final _id_currentValue = jniAccessors.getMethodIDOf(
@@ -455,7 +466,7 @@
   ///@return "Current value" associated with the current input context (state) of this parser
   ///@since 2.13 (added as replacement for older \#getCurrentValue()
   jni.JObject currentValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_currentValue, jni.JniCallType.objectType, []).object);
 
   static final _id_assignCurrentValue = jniAccessors.getMethodIDOf(
@@ -485,7 +496,7 @@
   /// Jackson 2.x versions (and removed from Jackson 3.0).
   ///@return Location of the last processed input unit (byte or character)
   jni.JObject getCurrentValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getCurrentValue, jni.JniCallType.objectType, []).object);
 
   static final _id_setCurrentValue = jniAccessors.getMethodIDOf(
@@ -558,8 +569,8 @@
   ///@param f Feature to enable
   ///@return This parser, to allow call chaining
   JsonParser enable(JsonParser_Feature f) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(reference, _id_enable,
-          jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(reference,
+          _id_enable, jni.JniCallType.objectType, [f.reference]).object);
 
   static final _id_disable = jniAccessors.getMethodIDOf(_classRef, "disable",
       "(Lcom/fasterxml/jackson/core/JsonParser\$Feature;)Lcom/fasterxml/jackson/core/JsonParser;");
@@ -572,8 +583,8 @@
   ///@param f Feature to disable
   ///@return This parser, to allow call chaining
   JsonParser disable(JsonParser_Feature f) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(reference, _id_disable,
-          jni.JniCallType.objectType, [f.reference]).object);
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(reference,
+          _id_disable, jni.JniCallType.objectType, [f.reference]).object);
 
   static final _id_configure = jniAccessors.getMethodIDOf(
       _classRef,
@@ -589,7 +600,7 @@
   ///@param state Whether to enable feature ({@code true}) or disable ({@code false})
   ///@return This parser, to allow call chaining
   JsonParser configure(JsonParser_Feature f, bool state) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_configure,
           jni.JniCallType.objectType,
@@ -644,7 +655,7 @@
   ///@since 2.3
   ///@deprecated Since 2.7, use \#overrideStdFeatures(int, int) instead
   JsonParser setFeatureMask(int mask) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_setFeatureMask, jni.JniCallType.objectType, [mask]).object);
 
   static final _id_overrideStdFeatures = jniAccessors.getMethodIDOf(_classRef,
@@ -666,7 +677,7 @@
   ///@return This parser, to allow call chaining
   ///@since 2.6
   JsonParser overrideStdFeatures(int values, int mask) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_overrideStdFeatures,
           jni.JniCallType.objectType,
@@ -703,7 +714,7 @@
   ///@return This parser, to allow call chaining
   ///@since 2.6
   JsonParser overrideFormatFeatures(int values, int mask) =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_overrideFormatFeatures,
           jni.JniCallType.objectType,
@@ -724,7 +735,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jsontoken_.JsonToken nextToken() =>
-      jsontoken_.JsonToken.fromRef(jniAccessors.callMethodWithArgs(
+      const jsontoken_.$JsonTokenType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_nextToken, jni.JniCallType.objectType, []).object);
 
   static final _id_nextValue = jniAccessors.getMethodIDOf(
@@ -750,7 +761,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jsontoken_.JsonToken nextValue() =>
-      jsontoken_.JsonToken.fromRef(jniAccessors.callMethodWithArgs(
+      const jsontoken_.$JsonTokenType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_nextValue, jni.JniCallType.objectType, []).object);
 
   static final _id_nextFieldName = jniAccessors.getMethodIDOf(_classRef,
@@ -794,7 +805,7 @@
   ///   JsonParseException for decoding problems
   ///@since 2.5
   jni.JString nextFieldName1() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_nextFieldName1, jni.JniCallType.objectType, []).object);
 
   static final _id_nextTextValue = jniAccessors.getMethodIDOf(
@@ -817,7 +828,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JString nextTextValue() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_nextTextValue, jni.JniCallType.objectType, []).object);
 
   static final _id_nextIntValue =
@@ -899,7 +910,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JObject nextBooleanValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_nextBooleanValue, jni.JniCallType.objectType, []).object);
 
   static final _id_skipChildren = jniAccessors.getMethodIDOf(
@@ -924,7 +935,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   JsonParser skipChildren() =>
-      JsonParser.fromRef(jniAccessors.callMethodWithArgs(
+      const $JsonParserType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_skipChildren, jni.JniCallType.objectType, []).object);
 
   static final _id_finishToken =
@@ -964,7 +975,7 @@
   ///   if the current token has been explicitly cleared.
   ///@since 2.8
   jsontoken_.JsonToken currentToken() =>
-      jsontoken_.JsonToken.fromRef(jniAccessors.callMethodWithArgs(
+      const jsontoken_.$JsonTokenType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_currentToken, jni.JniCallType.objectType, []).object);
 
   static final _id_currentTokenId =
@@ -995,8 +1006,10 @@
   ///@return Type of the token this parser currently points to,
   ///   if any: null before any tokens have been read, and
   jsontoken_.JsonToken getCurrentToken() =>
-      jsontoken_.JsonToken.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_getCurrentToken, jni.JniCallType.objectType, []).object);
+      const jsontoken_.$JsonTokenType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_getCurrentToken,
+          jni.JniCallType.objectType, []).object);
 
   static final _id_getCurrentTokenId =
       jniAccessors.getMethodIDOf(_classRef, "getCurrentTokenId", "()I");
@@ -1176,8 +1189,10 @@
   /// or if parser has been closed.
   ///@return Last cleared token, if any; {@code null} otherwise
   jsontoken_.JsonToken getLastClearedToken() =>
-      jsontoken_.JsonToken.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_getLastClearedToken, jni.JniCallType.objectType, []).object);
+      const jsontoken_.$JsonTokenType().fromRef(jniAccessors.callMethodWithArgs(
+          reference,
+          _id_getLastClearedToken,
+          jni.JniCallType.objectType, []).object);
 
   static final _id_overrideCurrentName = jniAccessors.getMethodIDOf(
       _classRef, "overrideCurrentName", "(Ljava/lang/String;)V");
@@ -1209,7 +1224,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JString getCurrentName() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getCurrentName, jni.JniCallType.objectType, []).object);
 
   static final _id_currentName = jniAccessors.getMethodIDOf(
@@ -1228,7 +1243,7 @@
   ///   JsonParseException for decoding problems
   ///@since 2.10
   jni.JString currentName() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_currentName, jni.JniCallType.objectType, []).object);
 
   static final _id_getText =
@@ -1245,8 +1260,9 @@
   ///   by \#nextToken() or other iteration methods)
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
-  jni.JString getText() => jni.JString.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_getText, jni.JniCallType.objectType, []).object);
+  jni.JString getText() =>
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_getText, jni.JniCallType.objectType, []).object);
 
   static final _id_getText1 =
       jniAccessors.getMethodIDOf(_classRef, "getText", "(Ljava/io/Writer;)I");
@@ -1305,8 +1321,9 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JArray<jni.JChar> getTextCharacters() =>
-      jni.JArray<jni.JChar>.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_getTextCharacters, jni.JniCallType.objectType, []).object);
+      const jni.JArrayType(jni.JCharType()).fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_getTextCharacters,
+              jni.JniCallType.objectType, []).object);
 
   static final _id_getTextLength =
       jniAccessors.getMethodIDOf(_classRef, "getTextLength", "()I");
@@ -1377,7 +1394,7 @@
   ///    (invalid format for numbers); plain IOException if underlying
   ///    content read fails (possible if values are extracted lazily)
   jni.JObject getNumberValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getNumberValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getNumberValueExact = jniAccessors.getMethodIDOf(
@@ -1401,7 +1418,7 @@
   ///    content read fails (possible if values are extracted lazily)
   ///@since 2.12
   jni.JObject getNumberValueExact() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getNumberValueExact, jni.JniCallType.objectType, []).object);
 
   static final _id_getNumberType = jniAccessors.getMethodIDOf(_classRef,
@@ -1417,8 +1434,8 @@
   ///@return Type of current number, if parser points to numeric token; {@code null} otherwise
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
-  JsonParser_NumberType getNumberType() =>
-      JsonParser_NumberType.fromRef(jniAccessors.callMethodWithArgs(
+  JsonParser_NumberType getNumberType() => const $JsonParser_NumberTypeType()
+      .fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_getNumberType, jni.JniCallType.objectType, []).object);
 
   static final _id_getByteValue =
@@ -1537,7 +1554,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JObject getBigIntegerValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getBigIntegerValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getFloatValue =
@@ -1601,7 +1618,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JObject getDecimalValue() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getDecimalValue, jni.JniCallType.objectType, []).object);
 
   static final _id_getBooleanValue =
@@ -1644,7 +1661,7 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JObject getEmbeddedObject() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getEmbeddedObject, jni.JniCallType.objectType, []).object);
 
   static final _id_getBinaryValue = jniAccessors.getMethodIDOf(_classRef,
@@ -1674,11 +1691,9 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JArray<jni.JByte> getBinaryValue(jni.JObject bv) =>
-      jni.JArray<jni.JByte>.fromRef(jniAccessors.callMethodWithArgs(
-          reference,
-          _id_getBinaryValue,
-          jni.JniCallType.objectType,
-          [bv.reference]).object);
+      const jni.JArrayType(jni.JByteType()).fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_getBinaryValue,
+              jni.JniCallType.objectType, [bv.reference]).object);
 
   static final _id_getBinaryValue1 =
       jniAccessors.getMethodIDOf(_classRef, "getBinaryValue", "()[B");
@@ -1693,8 +1708,9 @@
   ///@throws IOException for low-level read issues, or
   ///   JsonParseException for decoding problems
   jni.JArray<jni.JByte> getBinaryValue1() =>
-      jni.JArray<jni.JByte>.fromRef(jniAccessors.callMethodWithArgs(reference,
-          _id_getBinaryValue1, jni.JniCallType.objectType, []).object);
+      const jni.JArrayType(jni.JByteType()).fromRef(jniAccessors
+          .callMethodWithArgs(reference, _id_getBinaryValue1,
+              jni.JniCallType.objectType, []).object);
 
   static final _id_readBinaryValue = jniAccessors.getMethodIDOf(
       _classRef, "readBinaryValue", "(Ljava/io/OutputStream;)I");
@@ -1929,7 +1945,7 @@
   ///   JsonParseException for decoding problems
   ///@since 2.1
   jni.JString getValueAsString() =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(reference,
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(reference,
           _id_getValueAsString, jni.JniCallType.objectType, []).object);
 
   static final _id_getValueAsString1 = jniAccessors.getMethodIDOf(
@@ -1951,7 +1967,7 @@
   ///   JsonParseException for decoding problems
   ///@since 2.1
   jni.JString getValueAsString1(jni.JString def) =>
-      jni.JString.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_getValueAsString1,
           jni.JniCallType.objectType,
@@ -2015,7 +2031,7 @@
   ///   JsonParseException for decoding problems
   ///@since 2.3
   jni.JObject getObjectId() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_getObjectId, jni.JniCallType.objectType, []).object);
 
   static final _id_getTypeId = jniAccessors.getMethodIDOf(
@@ -2038,9 +2054,80 @@
   ///   JsonParseException for decoding problems
   ///@since 2.3
   jni.JObject getTypeId() =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+      const jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_getTypeId, jni.JniCallType.objectType, []).object);
 
+  static final _id_readValueAs = jniAccessors.getMethodIDOf(
+      _classRef, "readValueAs", "(Ljava/lang/Class;)Ljava/lang/Object;");
+
+  /// from: public T readValueAs(java.lang.Class<T> valueType)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  ///
+  /// Method to deserialize JSON content into a non-container
+  /// type (it can be an array type, however): typically a bean, array
+  /// or a wrapper type (like java.lang.Boolean).
+  /// __Note__: method can only be called if the parser has
+  /// an object codec assigned; this is true for parsers constructed
+  /// by <code>MappingJsonFactory</code> (from "jackson-databind" jar)
+  /// but not for JsonFactory (unless its <code>setCodec</code>
+  /// method has been explicitly called).
+  ///
+  /// This method may advance the event stream, for structured types
+  /// the current token will be the closing end marker (END_ARRAY,
+  /// END_OBJECT) of the bound structure. For non-structured Json types
+  /// (and for JsonToken\#VALUE_EMBEDDED_OBJECT)
+  /// stream is not advanced.
+  ///
+  /// Note: this method should NOT be used if the result type is a
+  /// container (java.util.Collection or java.util.Map.
+  /// The reason is that due to type erasure, key and value types
+  /// can not be introspected when using this method.
+  ///@param <T> Nominal type parameter for value type
+  ///@param valueType Java type to read content as (passed to ObjectCodec that
+  ///    deserializes content)
+  ///@return Java value read from content
+  ///@throws IOException if there is either an underlying I/O problem or decoding
+  ///    issue at format layer
+  T readValueAs<T extends jni.JObject>(
+          jni.JObjType<T> $T, jni.JObject valueType) =>
+      $T.fromRef(jniAccessors.callMethodWithArgs(reference, _id_readValueAs,
+          jni.JniCallType.objectType, [valueType.reference]).object);
+
+  static final _id_readValueAs1 = jniAccessors.getMethodIDOf(
+      _classRef,
+      "readValueAs",
+      "(Lcom/fasterxml/jackson/core/type/TypeReference;)Ljava/lang/Object;");
+
+  /// from: public T readValueAs(com.fasterxml.jackson.core.type.TypeReference<?> valueTypeRef)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  ///
+  /// Method to deserialize JSON content into a Java type, reference
+  /// to which is passed as argument. Type is passed using so-called
+  /// "super type token"
+  /// and specifically needs to be used if the root type is a
+  /// parameterized (generic) container type.
+  /// __Note__: method can only be called if the parser has
+  /// an object codec assigned; this is true for parsers constructed
+  /// by <code>MappingJsonFactory</code> (defined in 'jackson-databind' bundle)
+  /// but not for JsonFactory (unless its <code>setCodec</code>
+  /// method has been explicitly called).
+  ///
+  /// This method may advance the event stream, for structured types
+  /// the current token will be the closing end marker (END_ARRAY,
+  /// END_OBJECT) of the bound structure. For non-structured Json types
+  /// (and for JsonToken\#VALUE_EMBEDDED_OBJECT)
+  /// stream is not advanced.
+  ///@param <T> Nominal type parameter for value type
+  ///@param valueTypeRef Java type to read content as (passed to ObjectCodec that
+  ///    deserializes content)
+  ///@return Java value read from content
+  ///@throws IOException if there is either an underlying I/O problem or decoding
+  ///    issue at format layer
+  T readValueAs1<T extends jni.JObject>(
+          jni.JObjType<T> $T, jni.JObject valueTypeRef) =>
+      $T.fromRef(jniAccessors.callMethodWithArgs(reference, _id_readValueAs1,
+          jni.JniCallType.objectType, [valueTypeRef.reference]).object);
+
   static final _id_readValuesAs = jniAccessors.getMethodIDOf(
       _classRef, "readValuesAs", "(Ljava/lang/Class;)Ljava/util/Iterator;");
 
@@ -2055,8 +2142,9 @@
   ///@return Iterator for reading multiple Java values from content
   ///@throws IOException if there is either an underlying I/O problem or decoding
   ///    issue at format layer
-  jni.JObject readValuesAs(jni.JObject valueType) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+  jni.JObject readValuesAs<T extends jni.JObject>(
+          jni.JObjType<T> $T, jni.JObject valueType) =>
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_readValuesAs,
           jni.JniCallType.objectType,
@@ -2078,25 +2166,48 @@
   ///@return Iterator for reading multiple Java values from content
   ///@throws IOException if there is either an underlying I/O problem or decoding
   ///    issue at format layer
-  jni.JObject readValuesAs1(jni.JObject valueTypeRef) =>
-      jni.JObject.fromRef(jniAccessors.callMethodWithArgs(
+  jni.JObject readValuesAs1<T extends jni.JObject>(
+          jni.JObjType<T> $T, jni.JObject valueTypeRef) =>
+      jni.JObjectType().fromRef(jniAccessors.callMethodWithArgs(
           reference,
           _id_readValuesAs1,
           jni.JniCallType.objectType,
           [valueTypeRef.reference]).object);
+
+  static final _id_readValueAsTree = jniAccessors.getMethodIDOf(
+      _classRef, "readValueAsTree", "()Ljava/lang/Object;");
+
+  /// from: public T readValueAsTree()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  ///
+  /// Method to deserialize JSON content into equivalent "tree model",
+  /// represented by root TreeNode of resulting model.
+  /// For JSON Arrays it will an array node (with child nodes),
+  /// for objects object node (with child nodes), and for other types
+  /// matching leaf node type. Empty or whitespace documents are null.
+  ///@param <T> Nominal type parameter for result node type (to reduce need for casting)
+  ///@return root of the document, or null if empty or whitespace.
+  ///@throws IOException if there is either an underlying I/O problem or decoding
+  ///    issue at format layer
+  T readValueAsTree<T extends jni.JObject>(jni.JObjType<T> $T) =>
+      $T.fromRef(jniAccessors.callMethodWithArgs(reference, _id_readValueAsTree,
+          jni.JniCallType.objectType, []).object);
 }
 
-class _$JsonParserType extends jni.JType<JsonParser> {
-  const _$JsonParserType();
+class $JsonParserType extends jni.JObjType<JsonParser> {
+  const $JsonParserType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonParser;";
+
+  @override
+  JsonParser fromRef(jni.JObjectPtr ref) => JsonParser.fromRef(ref);
 }
 
 extension $JsonParserArray on jni.JArray<JsonParser> {
   JsonParser operator [](int index) {
-    return JsonParser.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonParserType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonParser value) {
@@ -2108,19 +2219,27 @@
 ///
 /// Enumeration that defines all on/off features for parsers.
 class JsonParser_Feature extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonParser_Feature.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef =
       jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonParser\$Feature");
-  JsonParser_Feature.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonParser_Feature> type = _$JsonParser_FeatureType();
+  static const type = $JsonParser_FeatureType();
+
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
       "values", "()[Lcom/fasterxml/jackson/core/JsonParser\$Feature;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.Feature[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<JsonParser_Feature> values() =>
-      jni.JArray<JsonParser_Feature>.fromRef(jniAccessors
+      const jni.JArrayType($JsonParser_FeatureType()).fromRef(jniAccessors
           .callStaticMethodWithArgs(
               _classRef, _id_values, jni.JniCallType.objectType, []).object);
 
@@ -2132,11 +2251,9 @@
   /// from: static public com.fasterxml.jackson.core.JsonParser.Feature valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
   static JsonParser_Feature valueOf(jni.JString name) =>
-      JsonParser_Feature.fromRef(jniAccessors.callStaticMethodWithArgs(
-          _classRef,
-          _id_valueOf,
-          jni.JniCallType.objectType,
-          [name.reference]).object);
+      const $JsonParser_FeatureType().fromRef(jniAccessors
+          .callStaticMethodWithArgs(_classRef, _id_valueOf,
+              jni.JniCallType.objectType, [name.reference]).object);
 
   static final _id_collectDefaults =
       jniAccessors.getStaticMethodIDOf(_classRef, "collectDefaults", "()I");
@@ -2171,17 +2288,21 @@
       reference, _id_getMask, jni.JniCallType.intType, []).integer;
 }
 
-class _$JsonParser_FeatureType extends jni.JType<JsonParser_Feature> {
-  const _$JsonParser_FeatureType();
+class $JsonParser_FeatureType extends jni.JObjType<JsonParser_Feature> {
+  const $JsonParser_FeatureType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonParser$Feature;";
+
+  @override
+  JsonParser_Feature fromRef(jni.JObjectPtr ref) =>
+      JsonParser_Feature.fromRef(ref);
 }
 
 extension $JsonParser_FeatureArray on jni.JArray<JsonParser_Feature> {
   JsonParser_Feature operator [](int index) {
-    return JsonParser_Feature.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonParser_FeatureType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonParser_Feature value) {
@@ -2194,20 +2315,27 @@
 /// Enumeration of possible "native" (optimal) types that can be
 /// used for numbers.
 class JsonParser_NumberType extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonParser_NumberType.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef = jniAccessors
       .getClassOf("com/fasterxml/jackson/core/JsonParser\$NumberType");
-  JsonParser_NumberType.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonParser_NumberType> type =
-      _$JsonParser_NumberTypeType();
+  static const type = $JsonParser_NumberTypeType();
+
   static final _id_values = jniAccessors.getStaticMethodIDOf(_classRef,
       "values", "()[Lcom/fasterxml/jackson/core/JsonParser\$NumberType;");
 
   /// from: static public com.fasterxml.jackson.core.JsonParser.NumberType[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<JsonParser_NumberType> values() =>
-      jni.JArray<JsonParser_NumberType>.fromRef(jniAccessors
+      const jni.JArrayType($JsonParser_NumberTypeType()).fromRef(jniAccessors
           .callStaticMethodWithArgs(
               _classRef, _id_values, jni.JniCallType.objectType, []).object);
 
@@ -2219,24 +2347,26 @@
   /// from: static public com.fasterxml.jackson.core.JsonParser.NumberType valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
   static JsonParser_NumberType valueOf(jni.JString name) =>
-      JsonParser_NumberType.fromRef(jniAccessors.callStaticMethodWithArgs(
-          _classRef,
-          _id_valueOf,
-          jni.JniCallType.objectType,
-          [name.reference]).object);
+      const $JsonParser_NumberTypeType().fromRef(jniAccessors
+          .callStaticMethodWithArgs(_classRef, _id_valueOf,
+              jni.JniCallType.objectType, [name.reference]).object);
 }
 
-class _$JsonParser_NumberTypeType extends jni.JType<JsonParser_NumberType> {
-  const _$JsonParser_NumberTypeType();
+class $JsonParser_NumberTypeType extends jni.JObjType<JsonParser_NumberType> {
+  const $JsonParser_NumberTypeType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonParser$NumberType;";
+
+  @override
+  JsonParser_NumberType fromRef(jni.JObjectPtr ref) =>
+      JsonParser_NumberType.fromRef(ref);
 }
 
 extension $JsonParser_NumberTypeArray on jni.JArray<JsonParser_NumberType> {
   JsonParser_NumberType operator [](int index) {
-    return JsonParser_NumberType.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonParser_NumberTypeType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonParser_NumberType value) {
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
index ae6e31c..e3aa9b6 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
@@ -24,6 +24,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_field
@@ -41,20 +42,29 @@
 /// Enumeration for basic token types used for returning results
 /// of parsing JSON content.
 class JsonToken extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  JsonToken.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
   static final _classRef =
       jniAccessors.getClassOf("com/fasterxml/jackson/core/JsonToken");
-  JsonToken.fromRef(jni.JObjectPtr ref) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<JsonToken> type = _$JsonTokenType();
+  static const type = $JsonTokenType();
+
   static final _id_values = jniAccessors.getStaticMethodIDOf(
       _classRef, "values", "()[Lcom/fasterxml/jackson/core/JsonToken;");
 
   /// from: static public com.fasterxml.jackson.core.JsonToken[] values()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<JsonToken> values() =>
-      jni.JArray<JsonToken>.fromRef(jniAccessors.callStaticMethodWithArgs(
-          _classRef, _id_values, jni.JniCallType.objectType, []).object);
+      const jni.JArrayType($JsonTokenType()).fromRef(jniAccessors
+          .callStaticMethodWithArgs(
+              _classRef, _id_values, jni.JniCallType.objectType, []).object);
 
   static final _id_valueOf = jniAccessors.getStaticMethodIDOf(_classRef,
       "valueOf", "(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonToken;");
@@ -62,8 +72,11 @@
   /// from: static public com.fasterxml.jackson.core.JsonToken valueOf(java.lang.String name)
   /// The returned object must be deleted after use, by calling the `delete` method.
   static JsonToken valueOf(jni.JString name) =>
-      JsonToken.fromRef(jniAccessors.callStaticMethodWithArgs(_classRef,
-          _id_valueOf, jni.JniCallType.objectType, [name.reference]).object);
+      const $JsonTokenType().fromRef(jniAccessors.callStaticMethodWithArgs(
+          _classRef,
+          _id_valueOf,
+          jni.JniCallType.objectType,
+          [name.reference]).object);
 
   static final _id_id = jniAccessors.getMethodIDOf(_classRef, "id", "()I");
 
@@ -76,16 +89,17 @@
 
   /// from: public final java.lang.String asString()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JString asString() => jni.JString.fromRef(jniAccessors.callMethodWithArgs(
-      reference, _id_asString, jni.JniCallType.objectType, []).object);
+  jni.JString asString() =>
+      const jni.JStringType().fromRef(jniAccessors.callMethodWithArgs(
+          reference, _id_asString, jni.JniCallType.objectType, []).object);
 
   static final _id_asCharArray =
       jniAccessors.getMethodIDOf(_classRef, "asCharArray", "()[C");
 
   /// from: public final char[] asCharArray()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JArray<jni.JChar> asCharArray() =>
-      jni.JArray<jni.JChar>.fromRef(jniAccessors.callMethodWithArgs(
+  jni.JArray<jni.JChar> asCharArray() => const jni.JArrayType(jni.JCharType())
+      .fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_asCharArray, jni.JniCallType.objectType, []).object);
 
   static final _id_asByteArray =
@@ -93,8 +107,8 @@
 
   /// from: public final byte[] asByteArray()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  jni.JArray<jni.JByte> asByteArray() =>
-      jni.JArray<jni.JByte>.fromRef(jniAccessors.callMethodWithArgs(
+  jni.JArray<jni.JByte> asByteArray() => const jni.JArrayType(jni.JByteType())
+      .fromRef(jniAccessors.callMethodWithArgs(
           reference, _id_asByteArray, jni.JniCallType.objectType, []).object);
 
   static final _id_isNumeric =
@@ -162,17 +176,20 @@
       reference, _id_isBoolean, jni.JniCallType.booleanType, []).boolean;
 }
 
-class _$JsonTokenType extends jni.JType<JsonToken> {
-  const _$JsonTokenType();
+class $JsonTokenType extends jni.JObjType<JsonToken> {
+  const $JsonTokenType();
 
   @override
   String get signature => r"Lcom/fasterxml/jackson/core/JsonToken;";
+
+  @override
+  JsonToken fromRef(jni.JObjectPtr ref) => JsonToken.fromRef(ref);
 }
 
 extension $JsonTokenArray on jni.JArray<JsonToken> {
   JsonToken operator [](int index) {
-    return JsonToken.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $JsonTokenType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, JsonToken value) {
diff --git a/pkgs/jnigen/test/package_resolver_test.dart b/pkgs/jnigen/test/package_resolver_test.dart
index cacb159..7645e12 100644
--- a/pkgs/jnigen/test/package_resolver_test.dart
+++ b/pkgs/jnigen/test/package_resolver_test.dart
@@ -14,12 +14,12 @@
 
 void main() {
   final resolver = FilePathResolver(
-      {
+      importMap: {
         'org.apache.pdfbox': 'package:pdfbox/pdfbox.dart',
         'android.os.Process': 'package:android/os.dart',
       },
-      'a.b.N',
-      {
+      currentClass: 'a.b.N',
+      inputClassNames: {
         'a.b.C',
         'a.b.c.D',
         'a.b.c.d.E',
diff --git a/pkgs/jnigen/test/regenerate_examples_test.dart b/pkgs/jnigen/test/regenerate_examples_test.dart
index 27dcdce..0f28fed 100644
--- a/pkgs/jnigen/test/regenerate_examples_test.dart
+++ b/pkgs/jnigen/test/regenerate_examples_test.dart
@@ -22,7 +22,8 @@
 ///
 /// [dartOutput] and [cOutput] are relative paths from example project dir.
 void testExample(String exampleName, String dartOutput, String? cOutput) {
-  test('Generate and compare bindings for $exampleName', () async {
+  test('Generate and compare bindings for $exampleName',
+      timeout: Timeout.factor(2), () async {
     final examplePath = join('example', exampleName);
     final configPath = join(examplePath, 'jnigen.yaml');
 
diff --git a/pkgs/jnigen/test/simple_package_test/generate.dart b/pkgs/jnigen/test/simple_package_test/generate.dart
index c433d43..99eb0c1 100644
--- a/pkgs/jnigen/test/simple_package_test/generate.dart
+++ b/pkgs/jnigen/test/simple_package_test/generate.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io';
 
+import 'package:logging/logging.dart';
 import 'package:path/path.dart';
 import 'package:jnigen/jnigen.dart';
 
@@ -24,6 +25,12 @@
   join(javaPrefix, 'simple_package', 'Example.java'),
   join(javaPrefix, 'pkg2', 'C2.java'),
   join(javaPrefix, 'pkg2', 'Example.java'),
+  join(javaPrefix, 'generics', 'MyStack.java'),
+  join(javaPrefix, 'generics', 'MyMap.java'),
+  join(javaPrefix, 'generics', 'GrandParent.java'),
+  join(javaPrefix, 'generics', 'StringStack.java'),
+  join(javaPrefix, 'generics', 'StringValuedMap.java'),
+  join(javaPrefix, 'generics', 'StringKeyedMap.java'),
 ];
 
 void compileJavaSources(String workingDir, List<String> files) async {
@@ -34,7 +41,7 @@
   }
 }
 
-Config getConfig() {
+Config getConfig([BindingsType bindingsType = BindingsType.cBased]) {
   compileJavaSources(javaPath, javaFiles);
   final cWrapperDir = Uri.directory(join(testRoot, "src"));
   final dartWrappersRoot = Uri.directory(join(testRoot, "lib"));
@@ -44,8 +51,11 @@
     classes: [
       'com.github.dart_lang.jnigen.simple_package',
       'com.github.dart_lang.jnigen.pkg2',
+      'com.github.dart_lang.jnigen.generics',
     ],
+    logLevel: Level.INFO,
     outputConfig: OutputConfig(
+      bindingsType: bindingsType,
       cConfig: CCodeOutputConfig(
         path: cWrapperDir,
         libraryName: 'simple_package',
diff --git a/pkgs/jnigen/test/simple_package_test/generated_files_test.dart b/pkgs/jnigen/test/simple_package_test/generated_files_test.dart
index 212e969..98a6d5f 100644
--- a/pkgs/jnigen/test/simple_package_test/generated_files_test.dart
+++ b/pkgs/jnigen/test/simple_package_test/generated_files_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:jnigen/jnigen.dart';
 import 'package:test/test.dart';
 import 'package:path/path.dart' hide equals;
 
@@ -16,4 +17,10 @@
       join(testRoot, "src"),
     );
   }); // test if generated file == expected file
+  test("Generate and analyze bindings for simple_package - pure dart",
+      () async {
+    await generateAndAnalyzeBindings(
+      getConfig(BindingsType.dartOnly),
+    );
+  }); // test if generated file == expected file
 }
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java
new file mode 100644
index 0000000..a12675a
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GrandParent.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+public class GrandParent<T> {
+  public T value;
+
+  public GrandParent(T value) {
+    this.value = value;
+  }
+
+  public Parent<String> stringParent() {
+    return new Parent<>(value, "Hello");
+  }
+
+  public <S> Parent<S> varParent(S nestedValue) {
+    return new Parent<>(value, nestedValue);
+  }
+
+  public static StaticParent<String> stringStaticParent() {
+    return new StaticParent<>("Hello");
+  }
+
+  public static <S> StaticParent<S> varStaticParent(S value) {
+    return new StaticParent<>(value);
+  }
+
+  public StaticParent<T> staticParentWithSameType() {
+    return new StaticParent<>(value);
+  }
+
+  // This doesn't have access to T
+  public static class StaticParent<S> {
+    public S value;
+
+    public StaticParent(S value) {
+      this.value = value;
+    }
+
+    public class Child<U> {
+      public S parentValue;
+      public U value;
+
+      public Child(S parentValue, U value) {
+        this.parentValue = parentValue;
+        this.value = value;
+      }
+    }
+  }
+
+  public class Parent<S> {
+    public T parentValue;
+    public S value;
+
+    public Parent(T parentValue, S value) {
+      this.parentValue = parentValue;
+      this.value = value;
+    }
+
+    public class Child<U> {
+      public T grandParentValue;
+      public S parentValue;
+      public U value;
+
+      public Child(T grandParentValue, S parentValue, U value) {
+        this.grandParentValue = grandParentValue;
+        this.parentValue = parentValue;
+        this.value = value;
+      }
+    }
+  }
+}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyMap.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyMap.java
new file mode 100644
index 0000000..21392ae
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyMap.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MyMap<K, V> {
+  public class MyEntry {
+    public K key;
+    public V value;
+
+    public MyEntry(K key, V value) {
+      this.key = key;
+      this.value = value;
+    }
+  }
+
+  private Map<K, V> map;
+
+  public MyMap() {
+    map = new HashMap<>();
+  }
+
+  public V get(K key) {
+    return map.get(key);
+  }
+
+  public V put(K key, V value) {
+    return map.put(key, value);
+  }
+
+  public MyStack<MyEntry> entryStack() {
+    var stack = new MyStack<MyEntry>();
+    map.entrySet().stream()
+        .map(e -> new MyEntry(e.getKey(), e.getValue()))
+        .forEach(e -> stack.push(e));
+    return stack;
+  }
+}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyStack.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyStack.java
new file mode 100644
index 0000000..fc85a3d
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/MyStack.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+import java.util.Stack;
+
+public class MyStack<T> {
+  private Stack<T> stack;
+
+  public MyStack() {
+    stack = new Stack<>();
+  }
+
+  public void push(T item) {
+    stack.push(item);
+  }
+
+  public T pop() {
+    return stack.pop();
+  }
+}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringKeyedMap.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringKeyedMap.java
new file mode 100644
index 0000000..1ca40aa
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringKeyedMap.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+public class StringKeyedMap<V> extends MyMap<String, V> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringStack.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringStack.java
new file mode 100644
index 0000000..87fd041
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringStack.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+public class StringStack extends MyStack<String> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringValuedMap.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringValuedMap.java
new file mode 100644
index 0000000..0f025b8
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/StringValuedMap.java
@@ -0,0 +1,3 @@
+package com.github.dart_lang.jnigen.generics;
+
+public class StringValuedMap<K> extends MyMap<K, String> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
index 86f8615..abd87c4 100644
--- a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
@@ -15,6 +15,12 @@
 
   private int internal = 0;
 
+  public Example() {}
+
+  public Example(int internal) {
+    this.internal = internal;
+  }
+
   static {
     aux = new Aux(true);
     num = 121;
diff --git a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
index a668bbc..841cabe 100644
--- a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
@@ -11,6 +11,7 @@
 // ignore_for_file: file_names
 // ignore_for_file: no_leading_underscores_for_local_identifiers
 // ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
 // ignore_for_file: unnecessary_cast
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
@@ -26,10 +27,16 @@
 
 /// from: com.github.dart_lang.jnigen.simple_package.Example
 class Example extends jni.JObject {
-  Example.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Example.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<Example> type = _$ExampleType();
+  static const type = $ExampleType();
 
   /// from: static public final int ON
   static const ON = 1;
@@ -44,7 +51,8 @@
 
   /// from: static public com.github.dart_lang.jnigen.simple_package.Example.Aux aux
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static Example_Aux get aux => Example_Aux.fromRef(_get_aux().object);
+  static Example_Aux get aux =>
+      const $Example_AuxType().fromRef(_get_aux().object);
   static final _set_aux = jniLookup<
           ffi.NativeFunction<
               jni.JThrowablePtr Function(
@@ -77,6 +85,14 @@
   /// from: public void <init>()
   Example() : super.fromRef(_ctor().object);
 
+  static final _ctor1 =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function(ffi.Int32)>>(
+              "Example__ctor1")
+          .asFunction<jni.JniResult Function(int)>();
+
+  /// from: public void <init>(int internal)
+  Example.ctor1(int internal) : super.fromRef(_ctor1(internal).object);
+
   static final _whichExample = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(
@@ -92,7 +108,8 @@
 
   /// from: static public com.github.dart_lang.jnigen.simple_package.Example.Aux getAux()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  static Example_Aux getAux() => Example_Aux.fromRef(_getAux().object);
+  static Example_Aux getAux() =>
+      const $Example_AuxType().fromRef(_getAux().object);
 
   static final _addInts = jniLookup<
               ffi.NativeFunction<jni.JniResult Function(ffi.Int32, ffi.Int32)>>(
@@ -109,7 +126,7 @@
   /// from: static public int[] getArr()
   /// The returned object must be deleted after use, by calling the `delete` method.
   static jni.JArray<jni.JInt> getArr() =>
-      jni.JArray<jni.JInt>.fromRef(_getArr().object);
+      const jni.JArrayType(jni.JIntType()).fromRef(_getArr().object);
 
   static final _addAll = jniLookup<
           ffi.NativeFunction<
@@ -127,7 +144,7 @@
 
   /// from: public com.github.dart_lang.jnigen.simple_package.Example getSelf()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  Example getSelf() => Example.fromRef(_getSelf(reference).object);
+  Example getSelf() => const $ExampleType().fromRef(_getSelf(reference).object);
 
   static final _getNum = jniLookup<
           ffi.NativeFunction<
@@ -173,17 +190,21 @@
   static void throwException() => _throwException().check();
 }
 
-class _$ExampleType extends jni.JType<Example> {
-  const _$ExampleType();
+class $ExampleType extends jni.JObjType<Example> {
+  const $ExampleType();
 
   @override
   String get signature =>
       r"Lcom/github/dart_lang/jnigen/simple_package/Example;";
+
+  @override
+  Example fromRef(jni.JObjectPtr ref) => Example.fromRef(ref);
 }
 
 extension $ExampleArray on jni.JArray<Example> {
   Example operator [](int index) {
-    return Example.fromRef(elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $ExampleType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, Example value) {
@@ -193,10 +214,17 @@
 
 /// from: com.github.dart_lang.jnigen.simple_package.Example$Aux
 class Example_Aux extends jni.JObject {
-  Example_Aux.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Example_Aux.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<Example_Aux> type = _$Example_AuxType();
+  static const type = $Example_AuxType();
+
   static final _get_value = jniLookup<
           ffi.NativeFunction<
               jni.JniResult Function(
@@ -245,18 +273,21 @@
   void setValue(bool value) => _setValue(reference, value ? 1 : 0).check();
 }
 
-class _$Example_AuxType extends jni.JType<Example_Aux> {
-  const _$Example_AuxType();
+class $Example_AuxType extends jni.JObjType<Example_Aux> {
+  const $Example_AuxType();
 
   @override
   String get signature =>
       r"Lcom/github/dart_lang/jnigen/simple_package/Example$Aux;";
+
+  @override
+  Example_Aux fromRef(jni.JObjectPtr ref) => Example_Aux.fromRef(ref);
 }
 
 extension $Example_AuxArray on jni.JArray<Example_Aux> {
   Example_Aux operator [](int index) {
-    return Example_Aux.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $Example_AuxType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, Example_Aux value) {
@@ -266,10 +297,17 @@
 
 /// from: com.github.dart_lang.jnigen.pkg2.C2
 class C2 extends jni.JObject {
-  C2.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  C2.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<C2> type = _$C2Type();
+  static const type = $C2Type();
+
   static final _get_CONSTANT =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "get_C2__CONSTANT")
@@ -293,16 +331,20 @@
   C2() : super.fromRef(_ctor().object);
 }
 
-class _$C2Type extends jni.JType<C2> {
-  const _$C2Type();
+class $C2Type extends jni.JObjType<C2> {
+  const $C2Type();
 
   @override
   String get signature => r"Lcom/github/dart_lang/jnigen/pkg2/C2;";
+
+  @override
+  C2 fromRef(jni.JObjectPtr ref) => C2.fromRef(ref);
 }
 
 extension $C2Array on jni.JArray<C2> {
   C2 operator [](int index) {
-    return C2.fromRef(elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $C2Type)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, C2 value) {
@@ -312,10 +354,17 @@
 
 /// from: com.github.dart_lang.jnigen.pkg2.Example
 class Example1 extends jni.JObject {
-  Example1.fromRef(ffi.Pointer<ffi.Void> ref) : super.fromRef(ref);
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Example1.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
 
   /// The type which includes information such as the signature of this class.
-  static const jni.JType<Example1> type = _$Example1Type();
+  static const type = $Example1Type();
+
   static final _ctor =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Example1__ctor")
           .asFunction<jni.JniResult Function()>();
@@ -333,20 +382,1146 @@
   int whichExample() => _whichExample(reference).integer;
 }
 
-class _$Example1Type extends jni.JType<Example1> {
-  const _$Example1Type();
+class $Example1Type extends jni.JObjType<Example1> {
+  const $Example1Type();
 
   @override
   String get signature => r"Lcom/github/dart_lang/jnigen/pkg2/Example;";
+
+  @override
+  Example1 fromRef(jni.JObjectPtr ref) => Example1.fromRef(ref);
 }
 
 extension $Example1Array on jni.JArray<Example1> {
   Example1 operator [](int index) {
-    return Example1.fromRef(
-        elementAt(index, jni.JniCallType.objectType).object);
+    return (elementType as $Example1Type)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
   }
 
   void operator []=(int index, Example1 value) {
     (this as jni.JArray<jni.JObject>)[index] = value;
   }
 }
+
+/// from: com.github.dart_lang.jnigen.generics.GrandParent
+class GrandParent<T extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $T,
+      );
+
+  final jni.JObjType<T> $T;
+
+  GrandParent.fromRef(
+    this.$T,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GrandParentType<T> type<T extends jni.JObject>(
+    jni.JObjType<T> $T,
+  ) {
+    return $GrandParentType(
+      $T,
+    );
+  }
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public T value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  T get value => $T.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JThrowablePtr Function(jni.JObjectPtr,
+                  ffi.Pointer<ffi.Void>)>>("set_GrandParent__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public T value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(T value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>)>>("GrandParent__ctor")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(T value)
+  GrandParent(this.$T, T value) : super.fromRef(_ctor(value.reference).object);
+
+  static final _stringParent = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>)>>("GrandParent__stringParent")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public com.github.dart_lang.jnigen.generics.GrandParent<T>.Parent<java.lang.String> stringParent()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  GrandParent_Parent<T, jni.JString> stringParent() =>
+      $GrandParent_ParentType($T, jni.JStringType())
+          .fromRef(_stringParent(reference).object);
+
+  static final _varParent = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("GrandParent__varParent")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public com.github.dart_lang.jnigen.generics.GrandParent<T>.Parent<S> varParent(S nestedValue)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  GrandParent_Parent<T, S> varParent<S extends jni.JObject>(
+          jni.JObjType<S> $S, S nestedValue) =>
+      $GrandParent_ParentType($T, $S)
+          .fromRef(_varParent(reference, nestedValue.reference).object);
+
+  static final _stringStaticParent =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+              "GrandParent__stringStaticParent")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: static public com.github.dart_lang.jnigen.generics.GrandParent.StaticParent<java.lang.String> stringStaticParent()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static GrandParent_StaticParent<jni.JString> stringStaticParent() =>
+      const $GrandParent_StaticParentType(jni.JStringType())
+          .fromRef(_stringStaticParent().object);
+
+  static final _varStaticParent = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>)>>("GrandParent__varStaticParent")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: static public com.github.dart_lang.jnigen.generics.GrandParent.StaticParent<S> varStaticParent(S value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static GrandParent_StaticParent<S> varStaticParent<S extends jni.JObject>(
+          jni.JObjType<S> $S, S value) =>
+      $GrandParent_StaticParentType($S)
+          .fromRef(_varStaticParent(value.reference).object);
+
+  static final _staticParentWithSameType = jniLookup<
+              ffi.NativeFunction<
+                  jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "GrandParent__staticParentWithSameType")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public com.github.dart_lang.jnigen.generics.GrandParent.StaticParent<T> staticParentWithSameType()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  GrandParent_StaticParent<T> staticParentWithSameType() =>
+      $GrandParent_StaticParentType($T)
+          .fromRef(_staticParentWithSameType(reference).object);
+}
+
+class $GrandParentType<T extends jni.JObject>
+    extends jni.JObjType<GrandParent<T>> {
+  final jni.JObjType<T> $T;
+
+  const $GrandParentType(
+    this.$T,
+  );
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/generics/GrandParent;";
+
+  @override
+  GrandParent<T> fromRef(jni.JObjectPtr ref) => GrandParent.fromRef($T, ref);
+}
+
+extension $GrandParentArray<T extends jni.JObject>
+    on jni.JArray<GrandParent<T>> {
+  GrandParent<T> operator [](int index) {
+    return (elementType as $GrandParentType<T>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, GrandParent<T> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.GrandParent$Parent
+class GrandParent_Parent<T extends jni.JObject, S extends jni.JObject>
+    extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $T,
+        $S,
+      );
+
+  final jni.JObjType<T> $T;
+  final jni.JObjType<S> $S;
+
+  GrandParent_Parent.fromRef(
+    this.$T,
+    this.$S,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GrandParent_ParentType<T, S>
+      type<T extends jni.JObject, S extends jni.JObject>(
+    jni.JObjType<T> $T,
+    jni.JObjType<S> $S,
+  ) {
+    return $GrandParent_ParentType(
+      $T,
+      $S,
+    );
+  }
+
+  static final _get_parentValue = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_Parent__parentValue")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public T parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  T get parentValue => $T.fromRef(_get_parentValue(reference).object);
+  static final _set_parentValue = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_Parent__parentValue")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public T parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set parentValue(T value) => _set_parentValue(reference, value.reference);
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_Parent__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  S get value => $S.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JThrowablePtr Function(jni.JObjectPtr,
+                  ffi.Pointer<ffi.Void>)>>("set_GrandParent_Parent__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(S value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("GrandParent_Parent__ctor")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(T parentValue, S value)
+  GrandParent_Parent(this.$T, this.$S, T parentValue, S value)
+      : super.fromRef(_ctor(parentValue.reference, value.reference).object);
+}
+
+class $GrandParent_ParentType<T extends jni.JObject, S extends jni.JObject>
+    extends jni.JObjType<GrandParent_Parent<T, S>> {
+  final jni.JObjType<T> $T;
+  final jni.JObjType<S> $S;
+
+  const $GrandParent_ParentType(
+    this.$T,
+    this.$S,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GrandParent$Parent;";
+
+  @override
+  GrandParent_Parent<T, S> fromRef(jni.JObjectPtr ref) =>
+      GrandParent_Parent.fromRef($T, $S, ref);
+}
+
+extension $GrandParent_ParentArray<T extends jni.JObject, S extends jni.JObject>
+    on jni.JArray<GrandParent_Parent<T, S>> {
+  GrandParent_Parent<T, S> operator [](int index) {
+    return (elementType as $GrandParent_ParentType<T, S>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, GrandParent_Parent<T, S> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.GrandParent$Parent$Child
+class GrandParent_Parent_Child<T extends jni.JObject, S extends jni.JObject,
+    U extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $T,
+        $S,
+        $U,
+      );
+
+  final jni.JObjType<T> $T;
+  final jni.JObjType<S> $S;
+  final jni.JObjType<U> $U;
+
+  GrandParent_Parent_Child.fromRef(
+    this.$T,
+    this.$S,
+    this.$U,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GrandParent_Parent_ChildType<T, S, U>
+      type<T extends jni.JObject, S extends jni.JObject, U extends jni.JObject>(
+    jni.JObjType<T> $T,
+    jni.JObjType<S> $S,
+    jni.JObjType<U> $U,
+  ) {
+    return $GrandParent_Parent_ChildType(
+      $T,
+      $S,
+      $U,
+    );
+  }
+
+  static final _get_grandParentValue = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_Parent_Child__grandParentValue")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public T grandParentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  T get grandParentValue => $T.fromRef(_get_grandParentValue(reference).object);
+  static final _set_grandParentValue = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_Parent_Child__grandParentValue")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public T grandParentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set grandParentValue(T value) =>
+      _set_grandParentValue(reference, value.reference);
+
+  static final _get_parentValue = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_Parent_Child__parentValue")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
+  static final _set_parentValue = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_Parent_Child__parentValue")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set parentValue(S value) => _set_parentValue(reference, value.reference);
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_Parent_Child__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  U get value => $U.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_Parent_Child__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(U value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("GrandParent_Parent_Child__ctor")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(T grandParentValue, S parentValue, U value)
+  GrandParent_Parent_Child(
+      this.$T, this.$S, this.$U, T grandParentValue, S parentValue, U value)
+      : super.fromRef(_ctor(grandParentValue.reference, parentValue.reference,
+                value.reference)
+            .object);
+}
+
+class $GrandParent_Parent_ChildType<T extends jni.JObject,
+        S extends jni.JObject, U extends jni.JObject>
+    extends jni.JObjType<GrandParent_Parent_Child<T, S, U>> {
+  final jni.JObjType<T> $T;
+  final jni.JObjType<S> $S;
+  final jni.JObjType<U> $U;
+
+  const $GrandParent_Parent_ChildType(
+    this.$T,
+    this.$S,
+    this.$U,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GrandParent$Parent$Child;";
+
+  @override
+  GrandParent_Parent_Child<T, S, U> fromRef(jni.JObjectPtr ref) =>
+      GrandParent_Parent_Child.fromRef($T, $S, $U, ref);
+}
+
+extension $GrandParent_Parent_ChildArray<
+    T extends jni.JObject,
+    S extends jni.JObject,
+    U extends jni.JObject> on jni.JArray<GrandParent_Parent_Child<T, S, U>> {
+  GrandParent_Parent_Child<T, S, U> operator [](int index) {
+    return (elementType as $GrandParent_Parent_ChildType<T, S, U>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, GrandParent_Parent_Child<T, S, U> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.GrandParent$StaticParent
+class GrandParent_StaticParent<S extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $S,
+      );
+
+  final jni.JObjType<S> $S;
+
+  GrandParent_StaticParent.fromRef(
+    this.$S,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GrandParent_StaticParentType<S> type<S extends jni.JObject>(
+    jni.JObjType<S> $S,
+  ) {
+    return $GrandParent_StaticParentType(
+      $S,
+    );
+  }
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_StaticParent__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  S get value => $S.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_StaticParent__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public S value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(S value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>)>>("GrandParent_StaticParent__ctor")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(S value)
+  GrandParent_StaticParent(this.$S, S value)
+      : super.fromRef(_ctor(value.reference).object);
+}
+
+class $GrandParent_StaticParentType<S extends jni.JObject>
+    extends jni.JObjType<GrandParent_StaticParent<S>> {
+  final jni.JObjType<S> $S;
+
+  const $GrandParent_StaticParentType(
+    this.$S,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent;";
+
+  @override
+  GrandParent_StaticParent<S> fromRef(jni.JObjectPtr ref) =>
+      GrandParent_StaticParent.fromRef($S, ref);
+}
+
+extension $GrandParent_StaticParentArray<S extends jni.JObject>
+    on jni.JArray<GrandParent_StaticParent<S>> {
+  GrandParent_StaticParent<S> operator [](int index) {
+    return (elementType as $GrandParent_StaticParentType<S>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, GrandParent_StaticParent<S> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.GrandParent$StaticParent$Child
+class GrandParent_StaticParent_Child<S extends jni.JObject,
+    U extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $S,
+        $U,
+      );
+
+  final jni.JObjType<S> $S;
+  final jni.JObjType<U> $U;
+
+  GrandParent_StaticParent_Child.fromRef(
+    this.$S,
+    this.$U,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GrandParent_StaticParent_ChildType<S, U>
+      type<S extends jni.JObject, U extends jni.JObject>(
+    jni.JObjType<S> $S,
+    jni.JObjType<U> $U,
+  ) {
+    return $GrandParent_StaticParent_ChildType(
+      $S,
+      $U,
+    );
+  }
+
+  static final _get_parentValue = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_StaticParent_Child__parentValue")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  S get parentValue => $S.fromRef(_get_parentValue(reference).object);
+  static final _set_parentValue = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_StaticParent_Child__parentValue")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public S parentValue
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set parentValue(S value) => _set_parentValue(reference, value.reference);
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_GrandParent_StaticParent_Child__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  U get value => $U.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+              ffi.NativeFunction<
+                  jni.JThrowablePtr Function(
+                      jni.JObjectPtr, ffi.Pointer<ffi.Void>)>>(
+          "set_GrandParent_StaticParent_Child__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public U value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(U value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+              ffi.NativeFunction<
+                  jni.JniResult Function(
+                      ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>>(
+          "GrandParent_StaticParent_Child__ctor")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(S parentValue, U value)
+  GrandParent_StaticParent_Child(this.$S, this.$U, S parentValue, U value)
+      : super.fromRef(_ctor(parentValue.reference, value.reference).object);
+}
+
+class $GrandParent_StaticParent_ChildType<S extends jni.JObject,
+        U extends jni.JObject>
+    extends jni.JObjType<GrandParent_StaticParent_Child<S, U>> {
+  final jni.JObjType<S> $S;
+  final jni.JObjType<U> $U;
+
+  const $GrandParent_StaticParent_ChildType(
+    this.$S,
+    this.$U,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child;";
+
+  @override
+  GrandParent_StaticParent_Child<S, U> fromRef(jni.JObjectPtr ref) =>
+      GrandParent_StaticParent_Child.fromRef($S, $U, ref);
+}
+
+extension $GrandParent_StaticParent_ChildArray<S extends jni.JObject,
+    U extends jni.JObject> on jni.JArray<GrandParent_StaticParent_Child<S, U>> {
+  GrandParent_StaticParent_Child<S, U> operator [](int index) {
+    return (elementType as $GrandParent_StaticParent_ChildType<S, U>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, GrandParent_StaticParent_Child<S, U> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.MyMap
+class MyMap<K extends jni.JObject, V extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $K,
+        $V,
+      );
+
+  final jni.JObjType<K> $K;
+  final jni.JObjType<V> $V;
+
+  MyMap.fromRef(
+    this.$K,
+    this.$V,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $MyMapType<K, V> type<K extends jni.JObject, V extends jni.JObject>(
+    jni.JObjType<K> $K,
+    jni.JObjType<V> $V,
+  ) {
+    return $MyMapType(
+      $K,
+      $V,
+    );
+  }
+
+  static final _ctor =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("MyMap__ctor")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  MyMap(this.$K, this.$V) : super.fromRef(_ctor().object);
+
+  static final _get0 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>>("MyMap__get0")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public V get(K key)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  V get0(K key) => $V.fromRef(_get0(reference, key.reference).object);
+
+  static final _put = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>>("MyMap__put")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public V put(K key, V value)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  V put(K key, V value) =>
+      $V.fromRef(_put(reference, key.reference, value.reference).object);
+
+  static final _entryStack = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>)>>("MyMap__entryStack")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public com.github.dart_lang.jnigen.generics.MyStack<com.github.dart_lang.jnigen.generics.MyMap<K,V>.MyEntry> entryStack()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  MyStack<MyMap_MyEntry<K, V>> entryStack() =>
+      $MyStackType($MyMap_MyEntryType($K, $V))
+          .fromRef(_entryStack(reference).object);
+}
+
+class $MyMapType<K extends jni.JObject, V extends jni.JObject>
+    extends jni.JObjType<MyMap<K, V>> {
+  final jni.JObjType<K> $K;
+  final jni.JObjType<V> $V;
+
+  const $MyMapType(
+    this.$K,
+    this.$V,
+  );
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/generics/MyMap;";
+
+  @override
+  MyMap<K, V> fromRef(jni.JObjectPtr ref) => MyMap.fromRef($K, $V, ref);
+}
+
+extension $MyMapArray<K extends jni.JObject, V extends jni.JObject>
+    on jni.JArray<MyMap<K, V>> {
+  MyMap<K, V> operator [](int index) {
+    return (elementType as $MyMapType<K, V>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, MyMap<K, V> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.MyMap$MyEntry
+class MyMap_MyEntry<K extends jni.JObject, V extends jni.JObject>
+    extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $K,
+        $V,
+      );
+
+  final jni.JObjType<K> $K;
+  final jni.JObjType<V> $V;
+
+  MyMap_MyEntry.fromRef(
+    this.$K,
+    this.$V,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $MyMap_MyEntryType<K, V>
+      type<K extends jni.JObject, V extends jni.JObject>(
+    jni.JObjType<K> $K,
+    jni.JObjType<V> $V,
+  ) {
+    return $MyMap_MyEntryType(
+      $K,
+      $V,
+    );
+  }
+
+  static final _get_key = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_MyMap_MyEntry__key")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public K key
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  K get key => $K.fromRef(_get_key(reference).object);
+  static final _set_key = jniLookup<
+          ffi.NativeFunction<
+              jni.JThrowablePtr Function(jni.JObjectPtr,
+                  ffi.Pointer<ffi.Void>)>>("set_MyMap_MyEntry__key")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public K key
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set key(K value) => _set_key(reference, value.reference);
+
+  static final _get_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+    jni.JObjectPtr,
+  )>>("get_MyMap_MyEntry__value")
+      .asFunction<
+          jni.JniResult Function(
+    jni.JObjectPtr,
+  )>();
+
+  /// from: public V value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  V get value => $V.fromRef(_get_value(reference).object);
+  static final _set_value = jniLookup<
+          ffi.NativeFunction<
+              jni.JThrowablePtr Function(jni.JObjectPtr,
+                  ffi.Pointer<ffi.Void>)>>("set_MyMap_MyEntry__value")
+      .asFunction<
+          jni.JThrowablePtr Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public V value
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set value(V value) => _set_value(reference, value.reference);
+
+  static final _ctor = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("MyMap_MyEntry__ctor")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(K key, V value)
+  MyMap_MyEntry(this.$K, this.$V, K key, V value)
+      : super.fromRef(_ctor(key.reference, value.reference).object);
+}
+
+class $MyMap_MyEntryType<K extends jni.JObject, V extends jni.JObject>
+    extends jni.JObjType<MyMap_MyEntry<K, V>> {
+  final jni.JObjType<K> $K;
+  final jni.JObjType<V> $V;
+
+  const $MyMap_MyEntryType(
+    this.$K,
+    this.$V,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/MyMap$MyEntry;";
+
+  @override
+  MyMap_MyEntry<K, V> fromRef(jni.JObjectPtr ref) =>
+      MyMap_MyEntry.fromRef($K, $V, ref);
+}
+
+extension $MyMap_MyEntryArray<K extends jni.JObject, V extends jni.JObject>
+    on jni.JArray<MyMap_MyEntry<K, V>> {
+  MyMap_MyEntry<K, V> operator [](int index) {
+    return (elementType as $MyMap_MyEntryType<K, V>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, MyMap_MyEntry<K, V> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.MyStack
+class MyStack<T extends jni.JObject> extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $T,
+      );
+
+  final jni.JObjType<T> $T;
+
+  MyStack.fromRef(
+    this.$T,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $MyStackType<T> type<T extends jni.JObject>(
+    jni.JObjType<T> $T,
+  ) {
+    return $MyStackType(
+      $T,
+    );
+  }
+
+  static final _ctor =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("MyStack__ctor")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  MyStack(this.$T) : super.fromRef(_ctor().object);
+
+  static final _push = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("MyStack__push")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void push(T item)
+  void push(T item) => _push(reference, item.reference).check();
+
+  static final _pop = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>)>>("MyStack__pop")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public T pop()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  T pop() => $T.fromRef(_pop(reference).object);
+}
+
+class $MyStackType<T extends jni.JObject> extends jni.JObjType<MyStack<T>> {
+  final jni.JObjType<T> $T;
+
+  const $MyStackType(
+    this.$T,
+  );
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/generics/MyStack;";
+
+  @override
+  MyStack<T> fromRef(jni.JObjectPtr ref) => MyStack.fromRef($T, ref);
+}
+
+extension $MyStackArray<T extends jni.JObject> on jni.JArray<MyStack<T>> {
+  MyStack<T> operator [](int index) {
+    return (elementType as $MyStackType<T>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, MyStack<T> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.StringKeyedMap
+class StringKeyedMap<V extends jni.JObject> extends MyMap<jni.JString, V> {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $V,
+      );
+
+  final jni.JObjType<V> $V;
+
+  StringKeyedMap.fromRef(
+    this.$V,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(const jni.JStringType(), $V, ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $StringKeyedMapType<V> type<V extends jni.JObject>(
+    jni.JObjType<V> $V,
+  ) {
+    return $StringKeyedMapType(
+      $V,
+    );
+  }
+
+  static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+          "StringKeyedMap__ctor")
+      .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  StringKeyedMap(this.$V)
+      : super.fromRef(const jni.JStringType(), $V, _ctor().object);
+}
+
+class $StringKeyedMapType<V extends jni.JObject>
+    extends jni.JObjType<StringKeyedMap<V>> {
+  final jni.JObjType<V> $V;
+
+  const $StringKeyedMapType(
+    this.$V,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/StringKeyedMap;";
+
+  @override
+  StringKeyedMap<V> fromRef(jni.JObjectPtr ref) =>
+      StringKeyedMap.fromRef($V, ref);
+}
+
+extension $StringKeyedMapArray<V extends jni.JObject>
+    on jni.JArray<StringKeyedMap<V>> {
+  StringKeyedMap<V> operator [](int index) {
+    return (elementType as $StringKeyedMapType<V>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, StringKeyedMap<V> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.StringStack
+class StringStack extends MyStack<jni.JString> {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  StringStack.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(const jni.JStringType(), ref);
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $StringStackType();
+
+  static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+          "StringStack__ctor")
+      .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  StringStack() : super.fromRef(const jni.JStringType(), _ctor().object);
+}
+
+class $StringStackType extends jni.JObjType<StringStack> {
+  const $StringStackType();
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/generics/StringStack;";
+
+  @override
+  StringStack fromRef(jni.JObjectPtr ref) => StringStack.fromRef(ref);
+}
+
+extension $StringStackArray on jni.JArray<StringStack> {
+  StringStack operator [](int index) {
+    return (elementType as $StringStackType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, StringStack value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
+
+/// from: com.github.dart_lang.jnigen.generics.StringValuedMap
+class StringValuedMap<K extends jni.JObject> extends MyMap<K, jni.JString> {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type(
+        $K,
+      );
+
+  final jni.JObjType<K> $K;
+
+  StringValuedMap.fromRef(
+    this.$K,
+    jni.JObjectPtr ref,
+  ) : super.fromRef($K, const jni.JStringType(), ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $StringValuedMapType<K> type<K extends jni.JObject>(
+    jni.JObjType<K> $K,
+  ) {
+    return $StringValuedMapType(
+      $K,
+    );
+  }
+
+  static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+          "StringValuedMap__ctor")
+      .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  StringValuedMap(this.$K)
+      : super.fromRef($K, const jni.JStringType(), _ctor().object);
+}
+
+class $StringValuedMapType<K extends jni.JObject>
+    extends jni.JObjType<StringValuedMap<K>> {
+  final jni.JObjType<K> $K;
+
+  const $StringValuedMapType(
+    this.$K,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/StringValuedMap;";
+
+  @override
+  StringValuedMap<K> fromRef(jni.JObjectPtr ref) =>
+      StringValuedMap.fromRef($K, ref);
+}
+
+extension $StringValuedMapArray<K extends jni.JObject>
+    on jni.JArray<StringValuedMap<K>> {
+  StringValuedMap<K> operator [](int index) {
+    return (elementType as $StringValuedMapType<K>)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, StringValuedMap<K> value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
diff --git a/pkgs/jnigen/test/simple_package_test/src/simple_package.c b/pkgs/jnigen/test/simple_package_test/src/simple_package.c
index aa7d521..ba17028 100644
--- a/pkgs/jnigen/test/simple_package_test/src/simple_package.c
+++ b/pkgs/jnigen/test/simple_package_test/src/simple_package.c
@@ -38,6 +38,23 @@
                      .exception = check_exception()};
 }
 
+jmethodID _m_Example__ctor1 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__ctor1(int32_t internal) {
+  load_env();
+  load_class_gr(&_c_Example,
+                "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__ctor1, "<init>", "(I)V");
+  if (_m_Example__ctor1 == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_Example, _m_Example__ctor1, internal);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
 jmethodID _m_Example__whichExample = NULL;
 FFI_PLUGIN_EXPORT
 JniResult Example__whichExample(jobject self_) {
@@ -424,3 +441,752 @@
       (*jniEnv)->CallIntMethod(jniEnv, self_, _m_Example1__whichExample);
   return (JniResult){.result = {.i = _result}, .exception = check_exception()};
 }
+
+// com.github.dart_lang.jnigen.generics.GrandParent
+jclass _c_GrandParent = NULL;
+
+jmethodID _m_GrandParent__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__ctor(jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent, &_m_GrandParent__ctor, "<init>",
+              "(Ljava/lang/Object;)V");
+  if (_m_GrandParent__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_GrandParent, _m_GrandParent__ctor, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_GrandParent__stringParent = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__stringParent(jobject self_) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent, &_m_GrandParent__stringParent, "stringParent",
+              "()Lcom/github/dart_lang/jnigen/generics/GrandParent$Parent;");
+  if (_m_GrandParent__stringParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_GrandParent__stringParent);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_GrandParent__varParent = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__varParent(jobject self_, jobject nestedValue) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent, &_m_GrandParent__varParent, "varParent",
+              "(Ljava/lang/Object;)Lcom/github/dart_lang/jnigen/generics/"
+              "GrandParent$Parent;");
+  if (_m_GrandParent__varParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_GrandParent__varParent, nestedValue);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_GrandParent__stringStaticParent = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__stringStaticParent() {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_static_method(
+      _c_GrandParent, &_m_GrandParent__stringStaticParent, "stringStaticParent",
+      "()Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent;");
+  if (_m_GrandParent__stringStaticParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallStaticObjectMethod(
+      jniEnv, _c_GrandParent, _m_GrandParent__stringStaticParent);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_GrandParent__varStaticParent = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__varStaticParent(jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_static_method(_c_GrandParent, &_m_GrandParent__varStaticParent,
+                     "varStaticParent",
+                     "(Ljava/lang/Object;)Lcom/github/dart_lang/jnigen/"
+                     "generics/GrandParent$StaticParent;");
+  if (_m_GrandParent__varStaticParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallStaticObjectMethod(
+      jniEnv, _c_GrandParent, _m_GrandParent__varStaticParent, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_GrandParent__staticParentWithSameType = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent__staticParentWithSameType(jobject self_) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(
+      _c_GrandParent, &_m_GrandParent__staticParentWithSameType,
+      "staticParentWithSameType",
+      "()Lcom/github/dart_lang/jnigen/generics/GrandParent$StaticParent;");
+  if (_m_GrandParent__staticParentWithSameType == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_GrandParent__staticParentWithSameType);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent__value(jobject self_) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent, &_f_GrandParent__value, "value",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref(
+      (*jniEnv)->GetObjectField(jniEnv, self_, _f_GrandParent__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent__value(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent,
+                "com/github/dart_lang/jnigen/generics/GrandParent");
+  if (_c_GrandParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent, &_f_GrandParent__value, "value",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_GrandParent__value, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.GrandParent$Parent
+jclass _c_GrandParent_Parent = NULL;
+
+jmethodID _m_GrandParent_Parent__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent_Parent__ctor(jobject parentValue, jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent_Parent,
+                "com/github/dart_lang/jnigen/generics/GrandParent$Parent");
+  if (_c_GrandParent_Parent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent_Parent, &_m_GrandParent_Parent__ctor, "<init>",
+              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+  if (_m_GrandParent_Parent__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_GrandParent_Parent,
+                           _m_GrandParent_Parent__ctor, parentValue, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_Parent__parentValue = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_Parent__parentValue(jobject self_) {
+  load_env();
+  load_class_gr(&_c_GrandParent_Parent,
+                "com/github/dart_lang/jnigen/generics/GrandParent$Parent");
+  if (_c_GrandParent_Parent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent, &_f_GrandParent_Parent__parentValue,
+             "parentValue", "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_Parent__parentValue));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_Parent__parentValue(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent_Parent,
+                "com/github/dart_lang/jnigen/generics/GrandParent$Parent");
+  if (_c_GrandParent_Parent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent, &_f_GrandParent_Parent__parentValue,
+             "parentValue", "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_GrandParent_Parent__parentValue,
+                            value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_Parent__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_Parent__value(jobject self_) {
+  load_env();
+  load_class_gr(&_c_GrandParent_Parent,
+                "com/github/dart_lang/jnigen/generics/GrandParent$Parent");
+  if (_c_GrandParent_Parent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent, &_f_GrandParent_Parent__value, "value",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref(
+      (*jniEnv)->GetObjectField(jniEnv, self_, _f_GrandParent_Parent__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_Parent__value(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(&_c_GrandParent_Parent,
+                "com/github/dart_lang/jnigen/generics/GrandParent$Parent");
+  if (_c_GrandParent_Parent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent, &_f_GrandParent_Parent__value, "value",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_GrandParent_Parent__value, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.GrandParent$Parent$Child
+jclass _c_GrandParent_Parent_Child = NULL;
+
+jmethodID _m_GrandParent_Parent_Child__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent_Parent_Child__ctor(jobject grandParentValue,
+                                         jobject parentValue,
+                                         jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent_Parent_Child, &_m_GrandParent_Parent_Child__ctor,
+              "<init>",
+              "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V");
+  if (_m_GrandParent_Parent_Child__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_GrandParent_Parent_Child,
+                                         _m_GrandParent_Parent_Child__ctor,
+                                         grandParentValue, parentValue, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_Parent_Child__grandParentValue = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_Parent_Child__grandParentValue(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child,
+             &_f_GrandParent_Parent_Child__grandParentValue, "grandParentValue",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_Parent_Child__grandParentValue));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_Parent_Child__grandParentValue(jobject self_,
+                                                         jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child,
+             &_f_GrandParent_Parent_Child__grandParentValue, "grandParentValue",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(
+      jniEnv, self_, _f_GrandParent_Parent_Child__grandParentValue, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_Parent_Child__parentValue = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_Parent_Child__parentValue(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child,
+             &_f_GrandParent_Parent_Child__parentValue, "parentValue",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_Parent_Child__parentValue));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_Parent_Child__parentValue(jobject self_,
+                                                    jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child,
+             &_f_GrandParent_Parent_Child__parentValue, "parentValue",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_,
+                            _f_GrandParent_Parent_Child__parentValue, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_Parent_Child__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_Parent_Child__value(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child, &_f_GrandParent_Parent_Child__value,
+             "value", "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_Parent_Child__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_Parent_Child__value(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_Parent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$Parent$Child");
+  if (_c_GrandParent_Parent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_Parent_Child, &_f_GrandParent_Parent_Child__value,
+             "value", "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_GrandParent_Parent_Child__value,
+                            value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.GrandParent$StaticParent
+jclass _c_GrandParent_StaticParent = NULL;
+
+jmethodID _m_GrandParent_StaticParent__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent_StaticParent__ctor(jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent");
+  if (_c_GrandParent_StaticParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent_StaticParent, &_m_GrandParent_StaticParent__ctor,
+              "<init>", "(Ljava/lang/Object;)V");
+  if (_m_GrandParent_StaticParent__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_GrandParent_StaticParent,
+                           _m_GrandParent_StaticParent__ctor, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_StaticParent__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_StaticParent__value(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent");
+  if (_c_GrandParent_StaticParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent, &_f_GrandParent_StaticParent__value,
+             "value", "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_StaticParent__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_StaticParent__value(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent");
+  if (_c_GrandParent_StaticParent == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent, &_f_GrandParent_StaticParent__value,
+             "value", "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_GrandParent_StaticParent__value,
+                            value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.GrandParent$StaticParent$Child
+jclass _c_GrandParent_StaticParent_Child = NULL;
+
+jmethodID _m_GrandParent_StaticParent_Child__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GrandParent_StaticParent_Child__ctor(jobject parentValue,
+                                               jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
+  if (_c_GrandParent_StaticParent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GrandParent_StaticParent_Child,
+              &_m_GrandParent_StaticParent_Child__ctor, "<init>",
+              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+  if (_m_GrandParent_StaticParent_Child__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(
+      jniEnv, _c_GrandParent_StaticParent_Child,
+      _m_GrandParent_StaticParent_Child__ctor, parentValue, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_StaticParent_Child__parentValue = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_StaticParent_Child__parentValue(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
+  if (_c_GrandParent_StaticParent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent_Child,
+             &_f_GrandParent_StaticParent_Child__parentValue, "parentValue",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_StaticParent_Child__parentValue));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_StaticParent_Child__parentValue(jobject self_,
+                                                          jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
+  if (_c_GrandParent_StaticParent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent_Child,
+             &_f_GrandParent_StaticParent_Child__parentValue, "parentValue",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(
+      jniEnv, self_, _f_GrandParent_StaticParent_Child__parentValue, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_GrandParent_StaticParent_Child__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_GrandParent_StaticParent_Child__value(jobject self_) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
+  if (_c_GrandParent_StaticParent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent_Child,
+             &_f_GrandParent_StaticParent_Child__value, "value",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref((*jniEnv)->GetObjectField(
+      jniEnv, self_, _f_GrandParent_StaticParent_Child__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_GrandParent_StaticParent_Child__value(jobject self_,
+                                                    jobject value) {
+  load_env();
+  load_class_gr(
+      &_c_GrandParent_StaticParent_Child,
+      "com/github/dart_lang/jnigen/generics/GrandParent$StaticParent$Child");
+  if (_c_GrandParent_StaticParent_Child == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_GrandParent_StaticParent_Child,
+             &_f_GrandParent_StaticParent_Child__value, "value",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_,
+                            _f_GrandParent_StaticParent_Child__value, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.MyMap
+jclass _c_MyMap = NULL;
+
+jmethodID _m_MyMap__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyMap__ctor() {
+  load_env();
+  load_class_gr(&_c_MyMap, "com/github/dart_lang/jnigen/generics/MyMap");
+  if (_c_MyMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyMap, &_m_MyMap__ctor, "<init>", "()V");
+  if (_m_MyMap__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_MyMap, _m_MyMap__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_MyMap__get0 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyMap__get0(jobject self_, jobject key) {
+  load_env();
+  load_class_gr(&_c_MyMap, "com/github/dart_lang/jnigen/generics/MyMap");
+  if (_c_MyMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyMap, &_m_MyMap__get0, "get",
+              "(Ljava/lang/Object;)Ljava/lang/Object;");
+  if (_m_MyMap__get0 == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_MyMap__get0, key);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_MyMap__put = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyMap__put(jobject self_, jobject key, jobject value) {
+  load_env();
+  load_class_gr(&_c_MyMap, "com/github/dart_lang/jnigen/generics/MyMap");
+  if (_c_MyMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyMap, &_m_MyMap__put, "put",
+              "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+  if (_m_MyMap__put == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_MyMap__put, key, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_MyMap__entryStack = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyMap__entryStack(jobject self_) {
+  load_env();
+  load_class_gr(&_c_MyMap, "com/github/dart_lang/jnigen/generics/MyMap");
+  if (_c_MyMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyMap, &_m_MyMap__entryStack, "entryStack",
+              "()Lcom/github/dart_lang/jnigen/generics/MyStack;");
+  if (_m_MyMap__entryStack == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_MyMap__entryStack);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.MyMap$MyEntry
+jclass _c_MyMap_MyEntry = NULL;
+
+jmethodID _m_MyMap_MyEntry__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyMap_MyEntry__ctor(jobject key, jobject value) {
+  load_env();
+  load_class_gr(&_c_MyMap_MyEntry,
+                "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
+  if (_c_MyMap_MyEntry == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyMap_MyEntry, &_m_MyMap_MyEntry__ctor, "<init>",
+              "(Ljava/lang/Object;Ljava/lang/Object;)V");
+  if (_m_MyMap_MyEntry__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_MyMap_MyEntry,
+                                         _m_MyMap_MyEntry__ctor, key, value);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jfieldID _f_MyMap_MyEntry__key = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_MyMap_MyEntry__key(jobject self_) {
+  load_env();
+  load_class_gr(&_c_MyMap_MyEntry,
+                "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
+  if (_c_MyMap_MyEntry == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_MyMap_MyEntry, &_f_MyMap_MyEntry__key, "key",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref(
+      (*jniEnv)->GetObjectField(jniEnv, self_, _f_MyMap_MyEntry__key));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_MyMap_MyEntry__key(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(&_c_MyMap_MyEntry,
+                "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
+  if (_c_MyMap_MyEntry == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_MyMap_MyEntry, &_f_MyMap_MyEntry__key, "key",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_MyMap_MyEntry__key, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_MyMap_MyEntry__value = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_MyMap_MyEntry__value(jobject self_) {
+  load_env();
+  load_class_gr(&_c_MyMap_MyEntry,
+                "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
+  if (_c_MyMap_MyEntry == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_MyMap_MyEntry, &_f_MyMap_MyEntry__value, "value",
+             "Ljava/lang/Object;");
+  jobject _result = to_global_ref(
+      (*jniEnv)->GetObjectField(jniEnv, self_, _f_MyMap_MyEntry__value));
+  return (JniResult){.result = {.l = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_MyMap_MyEntry__value(jobject self_, jobject value) {
+  load_env();
+  load_class_gr(&_c_MyMap_MyEntry,
+                "com/github/dart_lang/jnigen/generics/MyMap$MyEntry");
+  if (_c_MyMap_MyEntry == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_field(_c_MyMap_MyEntry, &_f_MyMap_MyEntry__value, "value",
+             "Ljava/lang/Object;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_MyMap_MyEntry__value, value);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.MyStack
+jclass _c_MyStack = NULL;
+
+jmethodID _m_MyStack__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyStack__ctor() {
+  load_env();
+  load_class_gr(&_c_MyStack, "com/github/dart_lang/jnigen/generics/MyStack");
+  if (_c_MyStack == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyStack, &_m_MyStack__ctor, "<init>", "()V");
+  if (_m_MyStack__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_MyStack, _m_MyStack__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_MyStack__push = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyStack__push(jobject self_, jobject item) {
+  load_env();
+  load_class_gr(&_c_MyStack, "com/github/dart_lang/jnigen/generics/MyStack");
+  if (_c_MyStack == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyStack, &_m_MyStack__push, "push", "(Ljava/lang/Object;)V");
+  if (_m_MyStack__push == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_MyStack__push, item);
+  return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_MyStack__pop = NULL;
+FFI_PLUGIN_EXPORT
+JniResult MyStack__pop(jobject self_) {
+  load_env();
+  load_class_gr(&_c_MyStack, "com/github/dart_lang/jnigen/generics/MyStack");
+  if (_c_MyStack == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_MyStack, &_m_MyStack__pop, "pop", "()Ljava/lang/Object;");
+  if (_m_MyStack__pop == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_MyStack__pop);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.StringKeyedMap
+jclass _c_StringKeyedMap = NULL;
+
+jmethodID _m_StringKeyedMap__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult StringKeyedMap__ctor() {
+  load_env();
+  load_class_gr(&_c_StringKeyedMap,
+                "com/github/dart_lang/jnigen/generics/StringKeyedMap");
+  if (_c_StringKeyedMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_StringKeyedMap, &_m_StringKeyedMap__ctor, "<init>", "()V");
+  if (_m_StringKeyedMap__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_StringKeyedMap, _m_StringKeyedMap__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.StringStack
+jclass _c_StringStack = NULL;
+
+jmethodID _m_StringStack__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult StringStack__ctor() {
+  load_env();
+  load_class_gr(&_c_StringStack,
+                "com/github/dart_lang/jnigen/generics/StringStack");
+  if (_c_StringStack == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_StringStack, &_m_StringStack__ctor, "<init>", "()V");
+  if (_m_StringStack__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_StringStack, _m_StringStack__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+// com.github.dart_lang.jnigen.generics.StringValuedMap
+jclass _c_StringValuedMap = NULL;
+
+jmethodID _m_StringValuedMap__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult StringValuedMap__ctor() {
+  load_env();
+  load_class_gr(&_c_StringValuedMap,
+                "com/github/dart_lang/jnigen/generics/StringValuedMap");
+  if (_c_StringValuedMap == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_StringValuedMap, &_m_StringValuedMap__ctor, "<init>", "()V");
+  if (_m_StringValuedMap__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_StringValuedMap,
+                                         _m_StringValuedMap__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}