আমি জাভা প্রতিবিম্ব ব্যবহার করে পদ্ধতি পরামিতি নাম পেতে পারি?


124

আমার যদি এর মতো ক্লাস থাকে:

public class Whatever
{
  public void aMethod(int aParam);
}

এটির কোনও উপায় আছে যা aMethodনামের সাথে একটি প্যারামিটার ব্যবহার করে aParam, এটি টাইপ int?


10
7 টি উত্তর এবং কেউ "পদ্ধতিতে স্বাক্ষর" শব্দটি উল্লেখ করেন নি। এটি মূলত যা আপনি প্রতিফলনের সাথে আত্মবিশ্বাস করতে পারেন, যার অর্থ: কোনও প্যারামিটারের নাম নেই।
ইওর্নলি

3
এটা হল সম্ভব, নীচের আমার উত্তর দেখার
flybywire

4
6 বছর পরে, জাভা 8-এ প্রতিবিম্বের মাধ্যমে এটি এখন সম্ভব , এই এসও উত্তরটি দেখুন
20

উত্তর:


90

সংক্ষেপ:

  • সংকলনের সময় ডিবাগের তথ্য অন্তর্ভুক্ত করা হলে প্যারামিটারের নাম পাওয়া সম্ভব। আরও তথ্যের জন্য এই উত্তর দেখুন
  • অন্যথায় প্যারামিটারের নাম পাওয়া সম্ভব নয়
  • পরামিতি টাইপ পাওয়া সম্ভব, ব্যবহার করে method.getParameterTypes()

সম্পাদকের জন্য স্বতঃসিদ্ধ কার্যকারিতা লেখার স্বার্থে (আপনি যে কোনও একটি মন্তব্যে বলেছেন) এখানে কয়েকটি বিকল্প রয়েছে:

  • ব্যবহারের arg0, arg1, arg2ইত্যাদি
  • ব্যবহারের intParam, stringParam, objectTypeParam, ইত্যাদি
  • উপরের সংমিশ্রণটি ব্যবহার করুন - অ-আদিম ধরণের জন্য প্রাক্তন এবং আদিম ধরণের জন্য দ্বিতীয়টি।
  • যুক্তির নাম মোটেও প্রদর্শন করবেন না - কেবল প্রকারের।

4
ইন্টারফেস দিয়ে কি এটি সম্ভব? আমি এখনও ইন্টারফেস প্যারামিটারের নাম পাওয়ার কোনও উপায় খুঁজে পাইনি।
সেমো

@ চেমো: আপনি কি ইন্টারফেসের প্যারামিটারের নাম পাওয়ার কোনও উপায় খুঁজে পেতে পেরেছিলেন?
বৈভব গুপ্ত

কেবল যোগ করার জন্য, তাই আদিম ধরণের থেকে উদ্বেগজনকভাবে অবজেক্ট তৈরি করার জন্য বসন্তের @ConstructorPererties টিকা প্রয়োজন।
ভরত

102

জাভা 8 এ আপনি নিম্নলিখিতগুলি করতে পারেন:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

public final class Methods {

    public static List<String> getParameterNames(Method method) {
        Parameter[] parameters = method.getParameters();
        List<String> parameterNames = new ArrayList<>();

        for (Parameter parameter : parameters) {
            if(!parameter.isNamePresent()) {
                throw new IllegalArgumentException("Parameter names are not present!");
            }

            String parameterName = parameter.getName();
            parameterNames.add(parameterName);
        }

        return parameterNames;
    }

    private Methods(){}
}

সুতরাং আপনার শ্রেণীর জন্য Whateverআমরা একটি ম্যানুয়াল পরীক্ষা করতে পারি:

import java.lang.reflect.Method;

public class ManualTest {
    public static void main(String[] args) {
        Method[] declaredMethods = Whatever.class.getDeclaredMethods();

        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().equals("aMethod")) {
                System.out.println(Methods.getParameterNames(declaredMethod));
                break;
            }
        }
    }
}

[aParam]আপনি যদি -parametersআপনার জাভা 8 সংকলকের কাছে তর্কটি পাস করেন তবে মুদ্রণ করা উচিত ।

ম্যাভেন ব্যবহারকারীদের জন্য:

<properties>
    <!-- PLUGIN VERSIONS -->
    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>

    <!-- OTHER PROPERTIES -->
    <java.version>1.8</java.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <!-- Original answer -->
                <compilerArgument>-parameters</compilerArgument>
                <!-- Or, if you use the plugin version >= 3.6.2 -->
                <parameters>true</parameters>
                <testCompilerArgument>-parameters</testCompilerArgument>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

আরও তথ্যের জন্য নিম্নলিখিত লিঙ্কগুলি দেখুন:

  1. অফিসিয়াল জাভা টিউটোরিয়াল: পদ্ধতির পরামিতিগুলির নাম অর্জন
  2. জেপি 118: রানটাইমটিতে প্যারামিটারের নামগুলিতে অ্যাক্সেস
  3. প্যারামিটার ক্লাসের জাভাদোক

2
আমি জানি না যে তারা সংকলক প্লাগইনটির জন্য যুক্তিগুলি পরিবর্তন করেছে কিনা, তবে সর্বশেষের (এখনকার 3.5.5.1) সাথে কনফিগারেশন বিভাগে কম্পাইলার আরোগুলি ব্যবহার করতে আমাকে করণীয় ছিল: <কনফিগারেশন> <কম্পাইলারআর্গ> <arg> - প্যারামিটারগুলি </ অর্গ> </compilerArgs> </
সর্বাধিক

15

প্যারানামার লাইব্রেরিটি একই সমস্যাটি সমাধানের জন্য তৈরি করা হয়েছিল।

এটি কয়েকটি ভিন্ন উপায়ে পদ্ধতির নাম নির্ধারণ করার চেষ্টা করে। যদি ক্লাসটি ডিবাগিংয়ের সাথে সংকলিত হয় তবে এটি শ্রেণীর বাইকোড পড়ে তথ্যগুলি বের করতে পারে।

আরেকটি উপায় হ'ল এটি একটি সঙ্কলিত হওয়ার পরে শ্রেণীর বাইকোডে একটি প্রাইভেট স্ট্যাটিক সদস্যকে ইনজেক্ট করা, তবে এটি একটি জারে রাখার আগে। এটি রানটাইমে ক্লাস থেকে এই তথ্যটি বের করার জন্য প্রতিবিম্ব ব্যবহার করে।

https://github.com/paul-hammant/paranamer

এই লাইব্রেরিটি ব্যবহার করতে আমার সমস্যা হয়েছিল তবে শেষ পর্যন্ত এটি কাজ করে ফেললাম। আমি সমস্যাগুলি রক্ষণাবেক্ষণকারীকে রিপোর্ট করার আশা করছি।


আমি একটি অ্যান্ড্রয়েড এপিপির ভিতরে প্যারানামার ব্যবহার করার চেষ্টা করছি। তবে আমি পাচ্ছিParameterNAmesNotFoundException
রিলওয়ান

10

org.springframework.core.DefaultParameterNameDiscoverer শ্রেণি দেখুন

DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));

10

হ্যাঁ.
কোড জাভা 8 অনুবর্তী কম্পাইলার দিয়ে কম্পাইল করা আবশ্যক ফর্ম্যাল প্যারামিটার নাম (চালু সঞ্চয় করতে বিকল্প -parameters বিকল্প)।
তারপরে এই কোড স্নিপেটে কাজ করা উচিত:

Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
   System.err.println(m.getName());
   for (Parameter p : m.getParameters()) {
    System.err.println("  " + p.getName());
   }
}

এটি চেষ্টা করে দেখেছি! যদিও একটি টিপ, এই সংকলক কনফিগারগুলি কার্যকর হওয়ার জন্য আমাকে আপনার প্রকল্পটি পুনরায় তৈরি করতে হয়েছিল।
#maelMakitla

5

আপনি প্রতিবিম্ব সহ পদ্ধতিটি পুনরুদ্ধার করতে পারেন এবং এর যুক্তির প্রকারগুলি সনাক্ত করতে পারেন। পরীক্ষা করে দেখুন http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29

তবে আপনি ব্যবহৃত যুক্তির নাম বলতে পারবেন না।


17
সত্যিই এটি সম্ভব। তাঁর প্রশ্নটি অবশ্য প্যারামিটারের নামটি সম্পর্কে স্পষ্টভাবে ছিল । টপিকটাইটেলটি পরীক্ষা করুন।
বালুসসি

1
এবং আমি উদ্ধৃতি দিয়েছি: "তবে আপনি ব্যবহৃত যুক্তির নাম বলতে পারবেন না।" শুধু আমার উত্তরটি পড়ুন -_-
জনসকো

3
প্রশ্নটি সেভাবে তৈরি করা হয়নি যা তিনি টাইপটি প্রাপ্তির বিষয়ে জানতেন না।
বালাসসি

3

এটি সম্ভব এবং স্প্রিং এমভিসি 3 এটি করে, তবে ঠিক কীভাবে তা দেখতে সময় নি নি।

ইউআরআই টেম্পলেট ভেরিয়েবলের সাথে মেথড প্যারামিটার নামের সাথে মিলে যাওয়া কেবল তখনই করা যেতে পারে যদি আপনার কোডটি ডিবাগিং সক্ষম করে সক্রিয় করা হয়। যদি আপনার ডিবাগিং সক্ষম না করা থাকে তবে একটি প্যারামিটারের সাথে ভেরিয়েবলের সমাধান হওয়া মানকে বাঁধতে আপনার অবশ্যই ইউআরআই টেম্পলেট ভেরিয়েবলের নামটি @PathVariable টীকাতে নির্দিষ্ট করতে হবে। উদাহরণ স্বরূপ:

বসন্তের ডকুমেন্টেশন থেকে নেওয়া


org.springframework.core.Conventions.getVariableName () তবে আমি অনুমান করি আপনার অবশ্যই নির্ভরতা হিসাবে cglib থাকতে হবে
Radu Toader

3

যদিও এটি সম্ভব নয় (যেমন অন্যেরা চিত্রিত করেছেন), আপনি প্যারামিটারের নামটি ধরে রাখার জন্য একটি টীকা ব্যবহার করতে পারেন এবং প্রতিচ্ছবি হলেও তা পেতে পারেন।

সবচেয়ে পরিষ্কার সমাধান নয়, তবে এটি কাজটি সম্পন্ন করে। কিছু ওয়েবসার্ভিস আসলে প্যারামিটারের নাম রাখার জন্য এটি করে (যেমন: গ্লাসফিশের সাথে ডাব্লুএসকে স্থাপন করা)



3

সুতরাং আপনার করা উচিত:

Whatever.declaredMethods
        .find { it.name == 'aMethod' }
        .parameters
        .collect { "$it.type : $it.name" }

তবে আপনি সম্ভবত এর মতো একটি তালিকা পাবেন:

["int : arg0"]

আমি বিশ্বাস করি এটি গ্রোভির 2.5++ এ স্থির হবে

সুতরাং বর্তমানে, উত্তরটি হ'ল:

  • যদি এটি গ্রোভির শ্রেণি হয়, তবে না, আপনি নামটি পেতে পারেন না তবে ভবিষ্যতে আপনার সক্ষম হওয়া উচিত।
  • যদি এটি জাভা 8 এর অধীনে একটি জাভা ক্লাস সংকলিত হয়, আপনি সক্ষম হবেন।

আরো দেখুন:


প্রতিটি পদ্ধতির জন্য, তারপর এর মতো কিছু:

Whatever.declaredMethods
        .findAll { !it.synthetic }
        .collect { method -> 
            println method
            method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
        }
        .each {
            println it
        }

আমি কোনও পদ্ধতির নামও উল্লেখ করতে চাই না aMethod। আমি এটি একটি ক্লাসে সমস্ত পদ্ধতির জন্য পেতে চাই।
স্নুপ

@ স্নুপ প্রতিটি অ সিনথেটিক পদ্ধতি পাওয়ার একটি উদাহরণ যোগ করেছেন
টিম_ইয়েটস

আমরা এর antlrজন্য প্যারামিটারের নাম পেতে পারি না ?
স্নুপ

2

আপনি যদি গ্রহণটি ব্যবহার করেন, কম্পাইলারকে পদ্ধতির পরামিতিগুলির তথ্য সঞ্চয় করার অনুমতি দিতে বেলো চিত্রটি দেখুন

এখানে চিত্র বর্ণনা লিখুন


2

@ বোজহো যেমন বলেছিলেন, সংকলনের সময় ডিবাগের তথ্য অন্তর্ভুক্ত করা থাকলে এটি করা সম্ভব। এখানে একটি ভাল উত্তর আছে ...

কোনও অবজেক্টের কনস্ট্রাক্টরের প্যারামিটারের নামগুলি (প্রতিবিম্ব) কীভাবে পাবেন? @ অ্যাডাম পেয়েন্টার দ্বারা

... এএসএম লাইব্রেরি ব্যবহার করছি। আপনি কীভাবে আপনার লক্ষ্য অর্জন করতে পারবেন তা দেখিয়ে আমি একটি উদাহরণ রেখেছি।

প্রথমত, এই নির্ভরতাগুলির সাথে একটি pom.xML দিয়ে শুরু করুন।

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>5.2</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

তারপরে, এই শ্রেণীর আপনার যা করা উচিত তা করা উচিত। কেবল স্থির পদ্ধতিতে প্রার্থনা করুন getParameterNames()

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;

public class ArgumentReflection {
    /**
     * Returns a list containing one parameter name for each argument accepted
     * by the given constructor. If the class was compiled with debugging
     * symbols, the parameter names will match those provided in the Java source
     * code. Otherwise, a generic "arg" parameter name is generated ("arg0" for
     * the first argument, "arg1" for the second...).
     * 
     * This method relies on the constructor's class loader to locate the
     * bytecode resource that defined its class.
     * 
     * @param theMethod
     * @return
     * @throws IOException
     */
    public static List<String> getParameterNames(Method theMethod) throws IOException {
        Class<?> declaringClass = theMethod.getDeclaringClass();
        ClassLoader declaringClassLoader = declaringClass.getClassLoader();

        Type declaringType = Type.getType(declaringClass);
        String constructorDescriptor = Type.getMethodDescriptor(theMethod);
        String url = declaringType.getInternalName() + ".class";

        InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url);
        if (classFileInputStream == null) {
            throw new IllegalArgumentException(
                    "The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: "
                            + url + ")");
        }

        ClassNode classNode;
        try {
            classNode = new ClassNode();
            ClassReader classReader = new ClassReader(classFileInputStream);
            classReader.accept(classNode, 0);
        } finally {
            classFileInputStream.close();
        }

        @SuppressWarnings("unchecked")
        List<MethodNode> methods = classNode.methods;
        for (MethodNode method : methods) {
            if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) {
                Type[] argumentTypes = Type.getArgumentTypes(method.desc);
                List<String> parameterNames = new ArrayList<String>(argumentTypes.length);

                @SuppressWarnings("unchecked")
                List<LocalVariableNode> localVariables = method.localVariables;
                for (int i = 1; i <= argumentTypes.length; i++) {
                    // The first local variable actually represents the "this"
                    // object if the method is not static!
                    parameterNames.add(localVariables.get(i).name);
                }

                return parameterNames;
            }
        }

        return null;
    }
}

এখানে ইউনিট পরীক্ষা সহ একটি উদাহরণ।

public class ArgumentReflectionTest {

    @Test
    public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {

        List<String> parameterNames = ArgumentReflection
                .getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
        assertEquals("firstName", parameterNames.get(0));
        assertEquals("lastName", parameterNames.get(1));
        assertEquals(2, parameterNames.size());

    }

    public static final class Clazz {

        public void callMe(String firstName, String lastName) {
        }

    }
}

আপনি গিটহাবের পুরো উদাহরণটি খুঁজে পেতে পারেন

আদেশ সহকারে

  • আমি @AdadPaynter থেকে মূল সমাধানটি সামান্য পরিবর্তন করেছি যাতে এটি মেথডসের জন্য কার্যকর হয়। আমি যদি সঠিকভাবে বুঝতে পারি তবে তার সমাধানটি কেবলমাত্র নির্মাণকারীদের সাথেই কাজ করে।
  • এই সমাধান staticপদ্ধতিগুলির সাথে কাজ করে না । এটি এ ক্ষেত্রে এএসএম দ্বারা ফিরিয়ে দেওয়া যুক্তিগুলির সংখ্যা পৃথক, তবে এটি এমন কিছু যা সহজেই স্থির করা যায়।

0

পরামিতি নামগুলি কেবল সংকলকটির জন্য দরকারী। কম্পাইলার যখন একটি ক্লাস ফাইল তৈরি করে, প্যারামিটারের নামগুলি অন্তর্ভুক্ত করা হয় না - কোনও পদ্ধতির যুক্তির তালিকায় কেবল তার আর্গুমেন্টের সংখ্যা এবং প্রকার থাকে। সুতরাং প্রতিবিম্বটি ব্যবহার করে প্যারামিটারের নামটি পুনরুদ্ধার করা অসম্ভব (আপনার প্রশ্নে ট্যাগ হিসাবে) - এটি কোথাও নেই।

তবে, যদি প্রতিবিম্বের ব্যবহারের কোনও কঠোর প্রয়োজন না হয় তবে আপনি সোর্স কোড থেকে সরাসরি এই তথ্যটি পুনরুদ্ধার করতে পারেন (ধরে নিবেন যে আপনার এটি রয়েছে)।


2
প্যারামিটারের নামটি কেবল একটি সংকলককেই কার্যকর নয় তারা একটি লিবারির গ্রাহকের কাছে তথ্যও সরবরাহ করে, ধরে নিই যে আমি একটি ক্লাস স্ট্রঞ্জডেট লিখেছিলাম যার পদ্ধতিতে অ্যাড ছিল (ইন্ট ডে, ইন্ট আওয়ার, ইন্ট মিনিট) আপনি যদি এটি ব্যবহার করতে যান এবং কোনও পদ্ধতি খুঁজে পান যোগ করুন (int arg0, int arg1, int arg2) এটি কতটা কার্যকর হবে?
ডেভিড ওয়াটারস

আমি দুঃখিত - আপনি আমার উত্তর পুরোপুরি ভুল বুঝেছেন। দয়া করে প্রশ্নের প্রসঙ্গে এটি পুনরায় পড়ুন।
ড্যানবেন

2
প্যারামিটারগুলির নাম অর্জন করাকে সেই পদ্ধতিটিকে প্রতিফলিত করে কল করা একটি দুর্দান্ত উপকার। এটি কেবল সংক্ষেপকারীর পক্ষে দরকারী নয়, এমনকি প্রশ্নের প্রসঙ্গেও।
corsiKa

Parameter names are only useful to the compiler.ভুল। Retrofit লাইব্রেরি দেখুন। এটি REST এপিআই অনুরোধ তৈরি করতে গতিশীল ইন্টারফেস ব্যবহার করে। এর বৈশিষ্ট্যগুলির মধ্যে একটি হ'ল ইউআরএল পাথগুলিতে স্থানধারকের নামগুলি সংজ্ঞায়িত করা এবং সেই স্থানধারকগুলিকে তাদের সংশ্লিষ্ট প্যারামিটারের নামগুলি প্রতিস্থাপন করার ক্ষমতা।
TheRealChx101

0

আমার 2 সেন্ট যোগ করতে; পরামিতি সম্পর্কিত তথ্য "ডিবাগিংয়ের জন্য" ক্লাস ফাইলে পাওয়া যায় যখন আপনি উত্সটি সংকলন করতে জাভ্যাক-জি ব্যবহার করেন। এবং এটি এপিটি-তে উপলব্ধ তবে আপনার একটি টীকা লাগবে যাতে আপনার কোনও ব্যবহার হয় না। (কেউ কেউ এখানে 4-5 বছর আগে অনুরূপ কিছু নিয়ে আলোচনা করেছেন: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )

সংক্ষেপে সংক্ষেপে আপনি সোর্স ফাইলগুলিতে সরাসরি কাজ না করা (এপিটি সংকলনের সময়ে যা করেন তার অনুরূপ) অবধি আপনি এটি পেতে পারবেন না।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.