描述问题
使用 scala 反射过程中,传入为值类型,比如Boolean时,会出现类型不匹配的情况
type mismatch;
found : Boolean(true)
required: Object
xxxMethod.invoke(new xxx, true)
解决方案
不多说,先放解决方案
package Reflect
class CopyToAnother {
private def findBoolean(boolean: Boolean): Unit = {
println(f"findBoolean ===> $boolean")
}
private def findString(string: String): Unit = {
println(f"findString ===> $string")
}
}
object CopyToAnother {
def main(args: Array[String]): Unit = {
val methods1 = classOf[CopyToAnother].getDeclaredMethods
for (m <- methods1) {
println(m)
}
/**
* private void Reflect.CopyToAnother.findBoolean(boolean)
* private void Reflect.CopyToAnother.findString(java.lang.String)
* public static void Reflect.CopyToAnother.main(java.lang.String[])
*/
// work
val findStringMethod = classOf[CopyToAnother].getDeclaredMethod("findString", classOf[String])
findStringMethod.setAccessible(true)
findStringMethod.invoke(new CopyToAnother, "ssss") // findString ===> ssss
// fail
val findBooleanMethod = classOf[CopyToAnother].getDeclaredMethod("findBoolean", classOf[Boolean])
findBooleanMethod.setAccessible(true)
findBooleanMethod.invoke(new CopyToAnother, true)
/**
* type mismatch;
* found : Boolean(true)
* required: Object
* findBooleanMethod.invoke(new CopyToAnother, true)
* */
// fix
val findBooleanMethodFix = classOf[CopyToAnother].getDeclaredMethod("findBoolean", classOf[Boolean])
findBooleanMethodFix.setAccessible(true)
findBooleanMethodFix.invoke(new CopyToAnother, Boolean.box(true)) // findBoolean ===> true
findBooleanMethodFix.invoke(new CopyToAnother, true.asInstanceOf[AnyRef]) // findBoolean ===> true
}
}
问题原因
观察一下报错,主要是类型不匹配导致,那么先看下scala
中invoke
源码;其实可以看到调用的是java
的invoke
;传入的参数第一个Object
是实例对象,第二个Object
是反射方法中参数的值,它的类型是 Object
,这就是问题的关键;其实是scala的Boolean和java中的Boolean代表含义并不一致造成的,换句话说,scala 中的Boolean更像是java中的boolean,而java的Boolean则是包装类
···
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
···
java数据结构
Scala 数据类型
*可以看到,scala
的Boolean
等并不是AnyRef
的子类,而AnyRef
恰好表示的是java.lang.Object*
;这也就讲通了为什么invoke
会报错,因为压根就是类型不匹配;而String
呢?scala
的String
用的就是Java
的String
啊!
Any
是所有类型的超类型,也称为顶级类 型。它定义了一些通用的方法如equals
、hashCode
和toString
。Any
有两个直接子类:AnyVal
和AnyRef
。AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
。 --ref:https://docs.scala-lang.org/zh-cn/tour/unified-types.html
既然知道原因了,那就简单了,无非是吧AnyVal转成AnyRef,以下就是两种思路,第一种很简单,强转:ture.asInstanceOf[AnyRef]
, 但是这个实在有点违和;还有一种就是装箱!自带方法
More
既然都看到这了,求知欲还挺强啊,哈哈;主要检索还是来自官网:https://www.scala-lang.org/api/2.12.8/scala/Boolean.html 官方就是这么解释的:scala中Boolean更像是Java中的基本数据类型boolean,是AnyVal的子类;
去源码里面搜一下 object Boolean
,好家伙,隔这手动装箱呢?你看java
多乖,会自动完成装箱,而scala
则需要使用装箱方法,Object Boolean
是Boolean
的伴生类,其中的方法类似java
的静态方法,直接Boolean.box(x)
,最终会返回java.lang.Boolean
的对象了
都到这里,那就肯定ok了吧,以下是java版本的反射,可以结合起来看
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CTA {
private void findBoolean(Boolean i) {
System.out.println(i);
}
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Method findBoolean = CTA.class.getDeclaredMethod("findBoolean", Boolean.class);
findBoolean.invoke(new CTA(), true); // true
}
}
Ref
- https://www.scala-lang.org/api/2.12.8/scala/Boolean$.html
- https://docs.scala-lang.org/zh-cn/tour/unified-types.html