Skip to content

Commit cb3cd34

Browse files
authored
[jni] Add the ability to check if a JObject is a subclass of a Java class. (#1943)
1 parent 6456d48 commit cb3cd34

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

pkgs/jni/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
instead use the default value of `build/jni_libs`.
1010
- Added `JArray.of`, which allows a `JArray` to be constructed from an
1111
`Iterable`.
12+
- Added `JObject.isA`, which checks whether a `JObject` is a instance of a
13+
java class.
1214

1315
## 0.13.0
1416

pkgs/jni/lib/src/jobject.dart

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ import '../jni.dart';
99
import 'jreference.dart';
1010
import 'types.dart';
1111

12+
// Error thrown when casting between incompatible `JObject` subclasses.
13+
final class CastError extends Error {
14+
final String _message;
15+
16+
CastError(this._message);
17+
18+
@override
19+
String toString() {
20+
return _message;
21+
}
22+
}
23+
1224
final class JObjectNullableType extends JObjType<JObject?> {
1325
@internal
1426
const JObjectNullableType();
@@ -125,22 +137,36 @@ class JObject {
125137
reference.release();
126138
}
127139

140+
/// Whether this object is of the given [type].
141+
///
142+
/// For example:
143+
///
144+
/// ```dart
145+
/// if (object.isA(JLong.type)) {
146+
/// final i = object.as(JLong.type).longValue;
147+
/// ...
148+
/// }
149+
/// ```
150+
bool isA<T extends JObject?>(JObjType<T> type) {
151+
final targetJClass = type.jClass.reference.toPointer();
152+
final canBeCasted = Jni.env.IsInstanceOf(reference.pointer, targetJClass);
153+
Jni.env.DeleteGlobalRef(targetJClass);
154+
return canBeCasted;
155+
}
156+
128157
/// Casts this object to another [type].
129158
///
130159
/// If [releaseOriginal] is `true`, the casted object will be released.
160+
///
161+
/// Throws [CastError] if this object is not an instance of [type].
131162
T as<T extends JObject?>(
132163
JObjType<T> type, {
133164
bool releaseOriginal = false,
134165
}) {
135-
assert(
136-
() {
137-
final jClass = type.jClass.reference.toPointer();
138-
final canBeCasted = Jni.env.IsInstanceOf(reference.pointer, jClass);
139-
Jni.env.DeleteGlobalRef(jClass);
140-
return canBeCasted;
141-
}(),
142-
'The object must be of type "${type.signature}".',
143-
);
166+
if (!isA(type)) {
167+
throw CastError('not a subtype of "${type.signature}"');
168+
}
169+
144170
if (releaseOriginal) {
145171
final ret = type.fromReference(JGlobalReference(reference.pointer));
146172
reference.setAsReleased();

pkgs/jni/test/jobject_test.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,24 @@ void run({required TestRunnerCallback testRunner}) {
277277
expect(maxLong, maxLongInJava);
278278
});
279279

280+
testRunner('isA returns true', () {
281+
final long = JLong(1);
282+
expect(long.isA(JLong.type), isTrue);
283+
expect(long.isA(JLong.nullableType), isTrue);
284+
expect(long.isA(JNumber.type), isTrue);
285+
expect(long.isA(JNumber.nullableType), isTrue);
286+
expect(long.isA(JObject.type), isTrue);
287+
expect(long.isA(JObject.nullableType), isTrue);
288+
});
289+
290+
testRunner('isA returns false', () {
291+
final long = JLong(1);
292+
expect(long.isA(JInteger.type), isFalse);
293+
expect(long.isA(JInteger.nullableType), isFalse);
294+
expect(long.isA(JString.type), isFalse);
295+
expect(long.isA(JString.nullableType), isFalse);
296+
});
297+
280298
testRunner('Casting correctly succeeds', () {
281299
final long = JLong(1);
282300
final long2 = long.as(JLong.type, releaseOriginal: true);
@@ -287,7 +305,8 @@ void run({required TestRunnerCallback testRunner}) {
287305
final long = JLong(1);
288306
expect(
289307
() => long.as(JInteger.type, releaseOriginal: true),
290-
throwsA(isA<AssertionError>()),
308+
throwsA(isA<CastError>().having(
309+
(e) => e.toString(), 'toString()', contains('java/lang/Integer'))),
291310
);
292311
});
293312

0 commit comments

Comments
 (0)