import{_ as c,r as t,o as i,c as p,b as s,d as e,e as n,w as a,a as d}from"./app-mh6GuRj9.js";const r={},u=s("h1",{id:"migration-to-kavaref",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#migration-to-kavaref","aria-hidden":"true"},"#"),e(" Migration to KavaRef")],-1),y={href:"https://github.com/HighCapable/YukiReflection",target:"_blank",rel:"noopener noreferrer"},v={href:"https://github.com/HighCapable/YukiHookAPI",target:"_blank",rel:"noopener noreferrer"},A=s("code",null,"KavaRef",-1),m=d(`
Notice
For YukiHookAPI
, you need to continue using its Hook API, and KavaRef
only includes Java reflection-related APIs.
The design concept of KavaRef
is similar to YukiReflection
, but not exactly the same. The following lists the differences between YukiReflection
and KavaRef
in basic reflection functions, which you can manually migrate based on.
For example, we have the following Java class.
The following example
public class MyClass {
private void myMethod(String content) {
System.out.println("Hello " + content + "!");
}
}
Here is a comparison of KavaRef
with YukiReflection
using examples.
The following example
// Assume that's your MyClass instance.
val myClass: MyClass
// Call and execute using KavaRef.
MyClass::class.resolve().firstMethod {
name = "myMethod"
parameters(String::class)
}.of(myClass).invoke("Hello, KavaRef!")
// Direct reference to instance.
myClass.asResolver().firstMethod {
name = "myMethod"
parameters(String::class)
}.invoke("Hello, KavaRef!")
// Assume that's your MyClass instance.
val myClass: MyClass
// Call and execute using YukiReflection.
MyClass::class.java.method {
name = "myMethod"
param(StringClass)
}.get(myClass).call("Hello, YukiReflection!")
// Direct reference to instance.
myClass.current().method {
name = "myMethod"
param(StringClass)
}.call("Hello, YukiReflection!")
KavaRef
starts reflection at any time; you need to use resolve()
to create a reflection scope. You no longer directly extend the related method
and constructor
methods to avoid polluting their scope.
KavaRef
provides the asResolver()
method to directly reference the reflection scope of the instance object, avoiding pollution caused by the creation of uncontrollable instance objects by the current()
method in YukiReflection
.
KavaRef
abandons the "Finder" design concept and uses the "Filter" design concept to obtain reflection results. "Find" is no longer finding, but "filtering".
KavaRef
canceled the design scheme defined in YukiReflection
for determining whether the Member
obtained in the resulting instance is multiple or single, and directly returns the entire List<MemberResolver>
. The example you see above uses firstMethod
to get the first matching MethodResolver
. If you need to get all matches, you can change to method
.
The conditional method name in MethodCondition
of KavaRef
has been modified from abbreviations such as param
used previously in YukiReflection
to parameters
to better align with the naming conventions of the Java reflection API.
KavaRef
no longer provides the param(...).order()
function in conditions, because this function itself is unstable. KavaRef
now uses an iterator for filtering, and the bytecode will no longer be in order, nor should bytecode be filtered by order. You can use firstMethod
, firstField
, or lastMethod
, lastField
, etc. to get the first or last matching result.
KavaRef
renames the get(instance)
method to of(instance)
because get(...)
may be confused with the get(...)
usage of Field
and lacks semantic clarity. At the same time, get(instance)
no longer gets the MethodFinder.Result.Instance
instance from something like MethodFinder.Result
, but uses of(instance)
to consistently operate and set the instance object to MemberResolver
.
Methods such as string()
, int()
, etc. in MethodFinder.Result.Instance
have been removed in KavaRef
. You can directly use get<String>()
, get<Int>()
, invoke<String>(...)
, invoke<Int>(...)
, etc. to get or call the corresponding type results.
Pay Attention
If you are looking for (filtering) Field
, you need to note that there may be semantic conflicts between KavaRef
and YukiReflection
in the acquisition method of Field
. Please pay special attention when migrating this part.
For example, get the static field of content
in MyClass
, in YukiReflection
, you would do this.
The following example
MyClass::class.java
.field { name = "content" } // Return FieldFinder.Result
.get() // Cannot be omitted, return FieldFinder.Result.Instance
.string() // value
In KavaRef
you need to do this.
The following example
MyClass::class.resolve()
.firstField { name = "content" } // Return FieldResolver<MyClass>
.get<String>() // value
As mentioned above, get(...)
is used to get the FieldFinder.Result.Instance
object in YukiReflection
, not the value. To get the value and process it as a specified type, you need to call string()
or cast<String>()
, and in KavaRef
, you use get<T>()
directly in MemberResolver
to get the value of the specified type. The usage of get(...)
of KavaRef
for get(...)
to of(...)
.
So the complete writing of the above example in KavaRef
should be.
The following example
// Since the call is a static instance, "of(null)" can be omitted.
MyClass::class.resolve()
.firstField { name = "content" } // It's already a call chain object FieldResolver<MyClass>
.of(null) // Can be omitted and return to the call chain object FieldResolver<MyClass>
.get<String>() // value
KavaRef
no longer provides call
methods for Method
, and is now merged uniformly into invoke
(with generic parameters). At the same time, KavaRef
defines the newInstance
method of Constructor
as create
(with generic parameters).
You may have noticed that the condition superClass()
is gone, it is still there, in KavaRef
it has been renamed to superclass()
, docking with the standard Java reflection API.
At the same time, KavaRef
extends KClass
, and you no longer need to use Some::class.java
to declare an instance of Class
in most scenarios.
Another design idea of KavaRef
is type safety. As long as you use KClass<T>
and Class<T>
that declare the generic type, it will be checked and converted to the corresponding type when of(instance)
and create(...)
, and type checking will be completed during coding to avoid runtime errors.
The following example
// Assume that's your MyClass instance.
val myClass: MyClass
// Using KavaRef to call and execute.
MyClass::class.resolve()
.firstMethod {
name = "myMethod"
parameters(String::class)
}
// Only instances of type MyClass can be passed in.
.of(myClass)
.invoke("Hello, KavaRef!")
KavaRef
and YukiReflection
are not much different in other functions and extended functions. KavaRef
separates these functions into a separate module.
The following functionality is provided in YukiReflection
but is not implemented and no longer provided in KavaRef
:
Preset reflection type constant classes, such as StringClass
, IntType
, etc
String::class
, Int::class
, etc. to instead it. For primitive types and wrapper classes, IntType
is equivalent to Int::class
, and IntClass
is equivalent to JInteger::class
DexClassFinder
function
RemedyPlan
and method { ... } .remedys { ... }
functions
ClassLoader.listOfClasses()
function
ClassLoader.searchClass()
function
Class.hasExtends
, Class.extends
, Class.implements
functions
A::class isSubclassOf B::class
Class.toJavaPrimitiveType()
function
"com.some.clazz".hasClass(loader)
function
loader.hasClass("com.some.clazz")
to instead itClass.hasField
, Class.hasMethod
, Class.hasConstructor
functions
Class.hasModifiers(...)
, Member.hasModifiers(...)
functions
Class.isPublic
, Member.isPublic
Class.generic()
, GenericClass
functions
Class.genericSuperclassTypeArguments()
. Due to design defects, no longer providedAny.current()
, CurrentClass
functions
Any.asResolver()
to instead itClass.buildOf(...)
function
Class.createInstance(...)
to instead itClass.allMethods()
, Class.allFields()
, Class.allConstructors()
functions
YLog
log function
KavaRef
no longer takes over the logs, you can use the corresponding platform implementation method and no longer providedKavaRef
is completely different from YukiReflection
in exception handling. The exception logic of KavaRef
will remain transparent by default. It no longer actively intercepts exceptions and prints error logs or even provides onNoSuchMethod
listener. When no valid members are filtered, KavaRef
will throw an exception directly unless you explicitly declare the condition as optional (consistent with YukiReflection
logic).
The following example
// Assume that's your MyClass instance.
val myClass: MyClass
// Using KavaRef to call and execute.
MyClass::class.resolve()
.optional() // Declare as optional, do not throw exceptions.
// Use firstMethodOrNull instead of firstMethod,
// because the NoSuchElementException of Kotlin itself will be thrown.
.firstMethodOrNull {
name = "doNonExistentMethod" // Assume that this method does not exist.
parameters(String::class)
}?.of(myClass)?.invoke("Hello, KavaRef!")