JAVA 8 反射获取参数名

码途紫霄狩
• 阅读 5691

前言

在JDK8之前javac编译是不会把构造器和方法的参数名编译进class中,如果需要获取参数名,可以在方法上加上注解,反射获取注解的值从而获取参数名,比如Jackson的@JsonCreator@JsonProperty 。而JDK8新增了这一个功能,可以直接调用java.lang.reflect.Parameter.getName()获取到,前提是javac需要添加-parameters这个参数。通常来说不建议这样做,因为这会增大.class和在JVM中会占用更多的内存。

正文

代码

直接上代码。

用来打印类信息
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

import static java.lang.System.out;

public class MethodParameterSpy {

    private static final String  fmt = "%24s: %s%n";


    public static void printClassConstructors(Class c) {
        Constructor[] allConstructors = c.getConstructors();
        out.format(fmt, "Number of constructors", allConstructors.length);
        for (Constructor currentConstructor : allConstructors) {
            printConstructor(currentConstructor);
        }
        Constructor[] allDeclConst = c.getDeclaredConstructors();
        out.format(fmt, "Number of declared constructors",
                allDeclConst.length);
        for (Constructor currentDeclConst : allDeclConst) {
            printConstructor(currentDeclConst);
        }
    }

    public static void printClassMethods(Class c) {
        Method[] allMethods = c.getDeclaredMethods();
        out.format(fmt, "Number of methods", allMethods.length);
        for (Method m : allMethods) {
            printMethod(m);
        }
    }

    public static void printConstructor(Constructor c) {
        out.format("%s%n", c.toGenericString());
        Parameter[] params = c.getParameters();
        out.format(fmt, "Number of parameters", params.length);
        for (int i = 0; i < params.length; i++) {
            printParameter(params[i]);
        }
    }

    public static void printMethod(Method m) {
        out.format("%s%n", m.toGenericString());
        out.format(fmt, "Return type", m.getReturnType());
        out.format(fmt, "Generic return type", m.getGenericReturnType());

        Parameter[] params = m.getParameters();
        for (int i = 0; i < params.length; i++) {
            printParameter(params[i]);
        }
    }

    public static void printParameter(Parameter p) {
        out.format(fmt, "Parameter class", p.getType());
        out.format(fmt, "Parameter name", p.getName());
        out.format(fmt, "Modifiers", p.getModifiers());
        out.format(fmt, "Is implicit?", p.isImplicit());
        out.format(fmt, "Is name present?", p.isNamePresent());
        out.format(fmt, "Is synthetic?", p.isSynthetic());
    }

    public static void main(String... args) {

        printClassConstructors(ExampleMethods.class);
        printClassMethods(ExampleMethods.class);
    }
}
包含各种类型方法的类
import java.util.*;

public class ExampleMethods<T> {

    public boolean simpleMethod(String stringParam, int intParam) {
        System.out.println("String: " + stringParam + ", integer: " + intParam);
        return true;
    }

    public int varArgsMethod(String... manyStrings) {
        return manyStrings.length;
    }

    public boolean methodWithList(List<String> listParam) {
        return listParam.isEmpty();
    }

    public <T> void genericMethod(T[] a, Collection<T> c) {
        System.out.println("Length of array: " + a.length);
        System.out.println("Size of collection: " + c.size());
    }

}

不带-parameters

不带-parameters运行结果:

  Number of constructors: 1
public ExampleMethods()
    Number of parameters: 0
Number of declared constructors: 1

# 构造器
public ExampleMethods()
    Number of parameters: 0
       Number of methods: 4
       
# 方法一
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
             Return type: boolean
     Generic return type: boolean
         Parameter class: class java.lang.String
          Parameter name: arg0
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false
         Parameter class: int
          Parameter name: arg1
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false
           
# 方法二
public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>)
             Return type: boolean
     Generic return type: boolean
         Parameter class: interface java.util.List
          Parameter name: arg0
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false

# 方法三
public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>)
             Return type: void
     Generic return type: void
         Parameter class: class [Ljava.lang.Object;
          Parameter name: arg0
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false
         Parameter class: interface java.util.Collection
          Parameter name: arg1
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false

# 方法四
public int ExampleMethods.varArgsMethod(java.lang.String...)
             Return type: int
     Generic return type: int
         Parameter class: class [Ljava.lang.String;
          Parameter name: arg0
               Modifiers: 0
            Is implicit?: false
        Is name present?: false
           Is synthetic?: false

可以看出Parameter name全都是arg0~argN,因为参数名在编译期已经丢失了。Is name present为false。

带-parameters

maven在pom.xml中添加
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>8</source>
                <target>8</target>
                <compilerArgument>-parameters</compilerArgument>
            </configuration>
        </plugin>
    </plugins>
</build>

命令行在javac 后面加 -parameters

运行结果
  Number of constructors: 1
public ExampleMethods()
    Number of parameters: 0
Number of declared constructors: 1

# 构造器
public ExampleMethods()
    Number of parameters: 0
       Number of methods: 4
           
# 方法一
public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>)
             Return type: boolean
     Generic return type: boolean
         Parameter class: interface java.util.List
          Parameter name: listParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

# 方法二
public int ExampleMethods.varArgsMethod(java.lang.String...)
             Return type: int
     Generic return type: int
         Parameter class: class [Ljava.lang.String;
          Parameter name: manyStrings
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

# 方法三
public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>)
             Return type: void
     Generic return type: void
         Parameter class: class [Ljava.lang.Object;
          Parameter name: a
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false
         Parameter class: interface java.util.Collection
          Parameter name: c
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false
                                 
# 方法四
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
             Return type: boolean
     Generic return type: boolean
         Parameter class: class java.lang.String
          Parameter name: stringParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false
         Parameter class: int
          Parameter name: intParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

这样就把参数名给打印出来了,Is name present为true。

留个问题

出于好奇,以十六进制打开ExampleMethods的class文件,maven项目在idea中build出来的class不管有没有带-parameters都会把参数名编译进去,但是多了MethodParameters这几个字眼。如下图:

JAVA 8 反射获取参数名

然后尝试直接用javac -parameters编译,打开后

JAVA 8 反射获取参数名

很明显是没有把参数名编译进去的。

好像找不到idea执行build的时候执行了什么,所有我猜测控制java.lang.reflect.Parameter.getName()返回是否真实的参数名就是在于MethodParameters这词,在jvm加载class时识别是否有MethodParameters,而决定是否加载参数名。

这仅是猜测,望大家相告是其真正的原理。
文中也可能存在错误,也望大家指出。

代码来源

上述代码全部来自#参考资料中的Obtaining Names of Method Parameters

参考资料

Obtaining Names of Method Parameters

点赞
收藏
评论区
推荐文章
陈发良 陈发良
4年前
总结:常用js方法
javascript函数注释/获取页面缓存@methodgetSession函数名@param参数名字符串类型,需要获取的key名@return变量名对象类型/img.onload定义和用法onload事件在图片加载完成后立即执行。判断设备constuserAgentnavigator
Wesley13 Wesley13
3年前
java构造方法
1.构造方法(函数)就是用来构造对象的方法,分为有参和无参两个类型。例如下面例子a.HellohellonewHello(); b.HellohellonewHello(Stringname); a是一个无参的构造方法,b是传入一个参数是string的构造方法。两者都是用来实例化对象。实例化对象的格式可以是:Class
红烧土豆泥 红烧土豆泥
4年前
软件设计模式-创建型模式之工厂进阶版
简介:所谓工厂模式,创建一个对象的接口,让子类决定实例化哪一个工厂类,使其创建过程延迟到了子类。所谓的万能工厂类,通过反射调用的方式,获取到子类对象,并实例化返回,此外本案例还通过重载的方式,允许了有参和无参两种获取到实例的方式。language/@author:demo@date:2021/8/7@describe:/public
Easter79 Easter79
3年前
SpringMVC(五):@RequestMapping下使用@RequestParam绑定请求参数值
在处理方法入参使用@RequestParam可以把请求参数传递给请求方法,@RequestParam包含的属性值:\value:参数名称\required:是否必须,默认为true,表示请求参数中必须包含对应的参数,否则抛出异常。\defaultValue:当请求参数缺少或者有请求参数但值为空时,值采用该设置值。示
Easter79 Easter79
3年前
SpringMVC 页面传递参数到controller的五种方式
一共是五种传参方式:一:直接将请求参数名作为Controller中方法的形参public Stringlogin(Stringusername,Stringpassword) :解释:括号中的参数必须与页面Form表单中的name名字相同二:使用@RequestParam绑定请求参数参数值举例:publicStri
Wesley13 Wesley13
3年前
C和C++的区别 04.函数重载
函数重载(Overload):用同一函数名定义不同的函数,当函数名和不同参数搭配时函数的意义不同。也就是说,函数重载就是,名字一样,参数不同。参数不同有三种:个数不同、类型不同、顺序不同。形参的名字和返回值相不相同无所谓。来看看编译器调用重载函数的准则:(看不懂或者觉得晕可以不看)将所有同名函数作为候选者尝试寻找可行的候选函数
Wesley13 Wesley13
3年前
Java反射技术概述
1.什么是Java反射?  就是正在运行,动态获取这个类的所有信息2.反射机制的作用  a.反编译:.class.java  b.通过反射机制,访问Java对象的属性,方法,构造方法等3.反射机制的应用场景  Jdbc加载驱动  SpringIOC实现  Java框架4.创建对象的两种方式  a.直
Stella981 Stella981
3年前
SpringBoot2 学习10 Controller接收参数的方式
地址传值@PathVariable获取路径参数。即url/{id}这种形式。?传值@RequestParam获取查询参数。即url?name这种形式用注解@RequestParam绑定请求参数到方法入参当请求参数username不存在时会有异常发生,可以通过设置属性requiredfalse解决,例如:@R
Stella981 Stella981
3年前
Sass 带参混合
本节我们来学习带参混合,Sass中混合可以接受参数,我们可以通过使用@include调用混合时给混合传参,来定义混合生成的精确样式。给混合传参混合可以用SassScript值作为参数,给定的参数被包括在混合中并且作为为变量提供给混合,既然是作为变量,那么定义参数时,参数名前面需要加一个$符号。当我们定义一个带参
Easter79 Easter79
3年前
SpringBoot2 学习10 Controller接收参数的方式
地址传值@PathVariable获取路径参数。即url/{id}这种形式。?传值@RequestParam获取查询参数。即url?name这种形式用注解@RequestParam绑定请求参数到方法入参当请求参数username不存在时会有异常发生,可以通过设置属性requiredfalse解决,例如:@R
Wesley13 Wesley13
3年前
Java高级特性—反射和动态代理
1).反射  通过反射的方式可以获取class对象中的属性、方法、构造函数等,一下是实例:2).动态代理  使用场景:      在之前的代码调用阶段,我们用action调用service的方法实现业务即可。    由于之前在service中实现的业务可能不能够满足当先客户的要求,需要我们重新修改servic