Repository: jni-rs/jni-rs (Rust)

Issue:
Improve JNIEnv#get_superclass()
The current implementation of JNIEnv#get_superclass() uses jni_non_null_call!() for GetSuperclass which is a little redundant since GetSuperclass does not throw any exception thus jni_unchecked!() could be used. But it may return NULL for an object/interface that doesn't have a superclass. The suggested behavior for this case is to still treat this as an error but probably it worth introducing another entity like Err(NoSuperClass("<class/interface name> can't have a super class")) instead of the NullPtr.


Discussion:
What about returning Option None instead?
> What about returning Option None instead?

This would make the most sense I'd think. Since not having a superclass isn't really an error. Option<T> where null translates to None would be most appropriate I'd say 



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9fec09fa..6c82f6b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 - `JNIEnv::get_string` checks that the given object is a `java.lang.String` instance to avoid undefined behaviour from the JNI implementation potentially aborting the program. ([#328](https://github.com/jni-rs/jni-rs/issues/328))
 - `JNIEnv::call_*method_unchecked` was marked `unsafe`, as passing improper argument types, or a bad number of arguments, can cause a JVM crash. ([#385](https://github.com/jni-rs/jni-rs/issues/385))
 - The `JNIEnv::new_object_unchecked` function now takes arguments as `&[jni::sys::jvalue]` to avoid allocating, putting it inline with changes to `JniEnv::call_*_unchecked` from 0.20.0 ([#382](https://github.com/jni-rs/jni-rs/pull/382))
+- The `get_superclass` function now returns an Option instead of a null pointer if the class has no superclass ([#151](https://github.com/jni-rs/jni-rs/issues/151))
 
 ## [0.20.0] — 2022-10-17
 
diff --git a/src/wrapper/jnienv.rs b/src/wrapper/jnienv.rs
index c74401e8..db98a06f 100644
--- a/src/wrapper/jnienv.rs
+++ b/src/wrapper/jnienv.rs
@@ -158,20 +158,26 @@ impl<'a> JNIEnv<'a> {
         Ok(unsafe { JClass::from_raw(class) })
     }
 
-    /// Returns the superclass for a particular class OR `JObject::null()` for `java.lang.Object` or
-    /// an interface. As with `find_class`, takes a descriptor.
-    pub fn get_superclass<'c, T>(&self, class: T) -> Result<JClass<'a>>
+    /// Returns the superclass for a particular class. Returns None for `java.lang.Object` or
+    /// an interface. As with [Self::find_class], takes a descriptor
+    ///
+    /// # Errors
+    ///
+    /// If a JNI call fails
+    pub fn get_superclass<'c, T>(&self, class: T) -> Result<Option<JClass<'a>>>
     where
         T: Desc<'a, JClass<'c>>,
     {
         let class = class.lookup(self)?;
-        Ok(unsafe {
-            JClass::from_raw(jni_non_void_call!(
+        let superclass = unsafe {
+            JClass::from_raw(jni_unchecked!(
                 self.internal,
                 GetSuperclass,
                 class.into_raw()
             ))
-        })
+        };
+
+        Ok((!superclass.is_null()).then_some(superclass))
     }
 
     /// Tests whether class1 is assignable from class2.


Test patch:
diff --git a/tests/jni_api.rs b/tests/jni_api.rs
index e9eed013..b1642bf0 100644
--- a/tests/jni_api.rs
+++ b/tests/jni_api.rs
@@ -889,7 +889,7 @@ fn get_super_class_ok() {
     let env = attach_current_thread();
     let result = env.get_superclass(ARRAYLIST_CLASS);
     assert!(result.is_ok());
-    assert!(!result.unwrap().is_null());
+    assert!(result.unwrap().is_some());
 }
 
 #[test]
@@ -897,7 +897,7 @@ fn get_super_class_null() {
     let env = attach_current_thread();
     let result = env.get_superclass("java/lang/Object");
     assert!(result.is_ok());
-    assert!(result.unwrap().is_null());
+    assert!(result.unwrap().is_none());
 }
 
 #[test]