下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

编程语言如何获取方法参数具体名称?

作者:小姐姐养的狗     来源: 小姐姐味道点击数:854发布时间: 2020-07-13 16:52:13

标签: 编程语言视频java视频JDk

大神带你学编程,欢迎选课

java如何获取方法参数具体名称?这是个好问题!编程语言往往使程序员能够比使用机器语言更准确地表达他们所想表达的目的。对那些从事计算机科学的人来说,懂得程序设计语言是十分重要的,因为在当今所有的计算都需要程序设计语言才能完成。

 默认情况下,我们是无法获取方法中参数名称的。通过反射机制,也只能得到参数的顺序以及一些没有意义的变量:arg0、arg1等等。

但我们又确实需要这部分信息。比如IDE的自动提示,文档化服务接口的详细信息等。

这是因为,这些变量的名字,根本就没有编译进class文件中,它不可能凭空产生。

编程语言如何获取方法参数具体名称?_编程语言视频_Python视频_JDk_课课家

JDk 8之后,可以通过在编译时指定-parameters选项,将方法的参数名记入class文件,并在运行时通过反射机制获取相关信息。

如果你的项目是实用maven构建,那么就可以加入几行配置,追加参数。

  1. <plugin>   
  2.     <artifactId>maven-compiler-plugin</artifactId>   
  3.     <version>3.8.0</version>   
  4.     <configuration>   
  5.         <source>1.8</source>   
  6.         <target>1.8</target>   
  7.         <encoding>utf8</encoding>   
  8.         <compilerArgs>   
  9.             <arg>-parameters</arg>   
  10.         </compilerArgs>   
  11.     </configuration>   
  12. </plugin>   

如果是用的IDEA等编辑器,也可以通过设置界面进行配置。不过不推荐这样,因为你的这些配置不好进行共享。

在普通Java项目里,就可以通过下面的方式来获取反射数据。Method.getParameters这个方法是新加的。

  1. public class Test { 
  2.    
  3.     public static void main(String[] args) throws Exception{ 
  4.         Class clazz = Class.forName("com.test.MethodParameterTest"); 
  5.         Method[] methods = clazz.getMethods(); 
  6.         Constructor[] constructors = clazz.getConstructors(); 
  7.         for (Constructor constructor : constructors) { 
  8.             System.out.println("+++" + constructor.getName()); 
  9.             Parameter[] parameters = constructor.getParameters(); 
  10.             for (Parameter parameter : parameters) { 
  11.                 printParameter(parameter); 
  12.             } 
  13.         } 
  14.    
  15.         System.out.println("------------------"); 
  16.         for (Method method : methods) { 
  17.             System.out.println(method.getName()); 
  18.             Parameter[] parameters = method.getParameters(); 
  19.             for (Parameter parameter : parameters) { 
  20.                 printParameter(parameter); 
  21.             } 
  22.         } 
  23.     } 
  24.    
  25.     private static void printParameter(Parameter parameter) { 
  26.         //参数名 
  27.         System.out.println("\\t\\t" + parameter.getName()); 
  28.         //是否在源码中隐式声明的参数名 
  29.         System.out.println("\\t\\t\\t implicit:" + parameter.isImplicit()); 
  30.         //类文件中,是否存在参数名 
  31.         System.out.println("\\t\\t\\t namePresent:" + parameter.isNamePresent()); 
  32.         //是否为虚构参数 
  33.         System.out.println("\\t\\t\\t synthetic:" + parameter.isSynthetic()); 
  34.         System.out.println("\\t\\t\\t VarArgs:" + parameter.isVarArgs()); 
  35.     } 

下面介绍几个方法的意义:

isImplicit()

参数是否为隐式声明在源文件中,比如内部类,默认构造函数(无参)其实在编译成class时将会把包含它的主类引用作为首个参数,此参数即为隐式声明。

如果为true,即表示有JDK编译器隐式生成在class文件中的方法参数,而source文件中并不可见。常规的普通方法,此值为false。

isNamePresent()

此参数在class文件中是否有此参数名;受制于在编译时是否指定了“-parameter”,对于指定此参数的编译文件,通常为true;对于JDK 内部类、默认编译的类,通常为false;此时你会发现,它们的参数名通常为表意名称:arg0、arg1等等,此时为false。

isSynthetic()

是否为“虚构”参数,如果为true,表示既不是“显式”声明、也不是隐式声明在源文件中的参数,比如enum类的“values()”、“valueOf(String)”这是编译器“虚构”的系统方法。

spring环境中,由于有工具类的支持,会更加方便一些。

  1. public class SpringTest { 
  2.    
  3.     private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); 
  4.    
  5.    
  6.     public static void main(String[] args) throws Exception{ 
  7.         Class clazz = Class.forName("com.test.MethodParameterTest"); 
  8.         Method[] methods = clazz.getMethods(); 
  9.         for (Method method : methods) { 
  10.             System.out.println(method.getName()); 
  11.             //JDK 1.8 + is better. 
  12.             String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); 
  13.             if (parameterNames == null) { 
  14.                 continue; 
  15.             } 
  16.             for (String pn : parameterNames) { 
  17.                 System.out.println("\\t\\t" + pn); 
  18.             } 
  19.         } 
  20.     } 

那Java版本低于1.8的时候,又是怎么获取的呢?我们可以参考Spring的LocalVariableTableParameterNameDiscoverer类。

  1. public String[] getParameterNames(Method method) { 
  2.         Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); 
  3.         return doGetParameterNames(originalMethod); 
  4. @Nullable 
  5. private String[] doGetParameterNames(Executable executable) { 
  6.         Class<?> declaringClass = executable.getDeclaringClass(); 
  7.         Map<Executable, String[]> map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass); 
  8.         return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null); 

最后就走到了inspectClass方法中。

  1. private Map<Executable, String[]> inspectClass(Class<?> clazz) { 
  2.         InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); 
  3.         if (is == null) { 
  4.             // We couldn't load the class file, which is not fatal as it 
  5.             // simply means this method of discovering parameter names won't work. 
  6.             if (logger.isDebugEnabled()) { 
  7.                 logger.debug("Cannot find '.class' file for class [" + clazz + 
  8.                         "] - unable to determine constructor/method parameter names"); 
  9.             } 
  10.             return NO_DEBUG_INFO_MAP; 
  11.         } 
  12.         try { 
  13.             ClassReader classReader = new ClassReader(is); 
  14.             Map<Executable, String[]> map = new ConcurrentHashMap<>(32); 
  15.             classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0); 
  16.             return map; 
  17.         } 
  18.         ... 

可以看到,这种情况下,Spring是通过直接读取class文件进行解析的。实际上是通过读取LocalVariableTable中的数据进行获取的。如果你编译的时候没有加入这些debug选项,同样也拿不到方法参数的具体名称。

总结一下

  • Java8以前,读取Class中的LocalVariableTable属性表,需要编译时加入参数-g或者-g:vars 获取方法局部变量调试信息;
  • Java8及其以后,通过java.lang.reflect.Parameter#getName即可获取,但需要编译时加入参数-parameters参数。
    在过去的几十年间,大量的编程语言被发明、被取代、被修改或组合在一起。尽管人们多次试图创造一种通用的程序设计语言,却没有一次尝试是成功的。之所以有那么多种不同的编程语言存在的原因是,编写程序的初衷其实也各不相同;新手与老手之间技术的差距非常大,而且有许多语言对新手来说太难学;还有,不同程序之间的运行成本(runtime cost)各不相同。
赞(9)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程