How to find the number of declared functions in a Java program - java

I am doing project in Core Java which identifies the similarity between two files, in that one part is to identify the declared functions length. I have tried the following code to find the declared methods in a given class.
import java.lang.reflect.*;
import java.io.*;
import java.lang.String.*;
public class Method1 {
private int f1(
Object p, int x) throws NullPointerException
{
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[])throws Exception
{
try {
Class cls = Class.forName("Anu");
int a;
Method methlist[]= cls.getDeclaredMethods();
for (int i = 0; i < methlist.length;i++) {
Method m = methlist[i];
System.out.println(methlist[i]);
System.out.println("name = " + (m.getName()).length());
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
But i have to find all the classes for a program. Shall i give input as a program since have to identify the declared methods in each class. Secondary it is working only if the given class is compiled, ie class file exist for given class.
Can any one help me for identifying the declared methods in the given program.
And i have to identify the comment lines in the program, please help me for that too.

You need to write you program to read the original code as you cannot only find the comments there. You can parse the text yourself to find comments and method signatures.
You might be able to google for libraries wich help you do this.

Using JavaCompiler class, reading file as string and execute it as below:
public class SampleTestCase {
public static void main(String[] args) {
String str = "public class sample {public static void doSomething() {System.out.println(\"Im here\");}}";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
SimpleJavaFileObject obj = new SourceString("sample", str);
Iterable<? extends JavaFileObject> compilationUnits = Arrays
.asList(obj);
CompilationTask task = compiler.getTask(null, null, diagnostics, null,
null, compilationUnits);
boolean success = task.call();
if (success) {
try {
Method[] declaredMethods = Class.forName("sample")
.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method.getName());
}
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
}
}
}
}
class SourceString extends SimpleJavaFileObject {
final String code;
SourceString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/')
+ Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}

Related

Renaming classes, including inner classes using ByteBuddy

I am trying to use ByteBuddy to rename all classes during the process-classes phase in a maven plugin. However despite many tries I am stuck on renaming the inner class. This is my last attempt:
String className = classNameOfFile(root, file);
try {
Class<?> clazz = cl.loadClass(className);
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
return;
}
var builder = new ByteBuddy().rebase(clazz).name(className + "Impl");
var unloaded = builder.make();
var loaded = unloaded.load(cl);
unloaded.saveIn(root);
Queue<Class<?>> queue = new LinkedList<>(Arrays.stream(clazz.getDeclaredClasses()).toList());
while (!queue.isEmpty()) {
var innerClass = queue.poll();
String name = innerClass.getSimpleName();
new ByteBuddy().rebase(innerClass).innerTypeOf(loaded.getLoaded()).name(className+"Impl$"+name+"Impl").make().saveIn(root);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
In here the top level class is changed successfully, however the inner class is not doesn't get defined and gives the following error:
Constructor public dev.rvr.MainImpl$CoolImpl() defines an illegal receiver class dev.rvr.Main
For reference, this is the class it is trying to change:
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
public TestingThing getTestingThing() {
return new TestingThing();
}
public Cool getCool() {
return new Cool();
}
public static class Cool{
public void cool(){
System.out.println("Cool");
}
public void cool2(){
System.out.println("Cool2");
}
public void cool3(){
System.out.println("Cool3");
}
}
}
I hope anyone can help me solving this issue, thanks in advance!
Byte Buddy is not a good tool for this. As Holger states in the comment, you need to change all classes that reference it in any way.
ASM has a Renamer for this purpose. You would need to use this. If you want to use Byte Buddy's build tool infrastructure, you can register such a transformer via visit on the builder API.

Groovy customize MetaClassImpl cause 'Could not find matching constructor'

Here is a hello world level groovy mop program, just want to customize MetaClassImpl using my own, but it seems groovy not allowed, here is my code:
MyMetaClassImpl.java
import groovy.lang.MetaClassImpl;
public class MyMetaClassImpl extends MetaClassImpl {
public MyMetaClassImpl(Class theClass) {
super(theClass);
}
#Override
public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
System.out.println("invoke here");
return super.invokeMethod(object, methodName, originalArguments);
}
}
Person.groovy
class Person {
String name
int age
String getHabit(String name) {
String habit = null
switch (name) {
case "ZhangSan":
habit = "soccer"
break
case "Lisi":
habit = "basketball"
break
default:
break
}
return habit
}
#Override
Object invokeMethod(String name, Object args) {
if (name == 'say') {
println "say method has been invoked"
return null;
}
super.invokeMethod(name, args)
}
}
PersonGroovyMain.groovy
import cn.crabime.java.meta.MyDelegateMetaClass
import cn.crabime.java.meta.MyMetaClassImpl
import cn.crabime.java.pogo.Person
import org.codehaus.groovy.runtime.InvokerHelper
class PersonGroovyMain {
static void main(String[] args) {
invokeNormal()
// cause error line
def p = new Person()
println p
}
static MetaClassImpl invokeNormal() {
MyMetaClassImpl norMetaClass = new MyMetaClassImpl(Person.class)
norMetaClass.initialize()
InvokerHelper.metaRegistry.setMetaClass(Person.class, norMetaClass)
return norMetaClass
}
}
Output error below:
Exception in thread "main" groovy.lang.GroovyRuntimeException: Could not find matching constructor for: cn.crabime.java.pogo.Person()
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1724)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1527)
at org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite.callConstructor(MetaClassConstructorSite.java:46)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:230)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:234)
at cn.crabime.java.main.PersonGroovyMain.main(PersonGroovyMain.groovy:17)
this question already puzzled me two days, and i don't want to use DelegatingMetaClass, hope somebody can help me out.
use constructor with 3 parameters
#groovy.transform.ToString
class A{ String name }
def meta = new MetaClassImpl(GroovySystem.getMetaClassRegistry(), A.class, null){
#Override
public Object invokeMethod(Object object, String methodName, Object arguments) {
if(methodName=='f1' && !arguments){
return object.name.toUpperCase()
}
return super.invokeMethod(object, methodName, arguments)
}
}
meta.initialize()
GroovySystem.getMetaClassRegistry().setMetaClass(A.class, meta)
def a = new A(name:'world')
println a.f1()

How can I find out which method a lambda applies to? [duplicate]

This question already has answers here:
Printing debug info on errors with java 8 lambda expressions
(2 answers)
How can I find the target of a Java8 method reference?
(2 answers)
Closed 3 years ago.
If a method in a library is called with a Java Lambda expression, these are often just wrapped method calls. Is it possible to find out what method that originally was, just for logging purposes? (Another question is about what object it applies to - this is specifically about the called method.)
class Foo {
private void doSomething() { ... }
public void doSomethingInTransaction() {
doInTransaction(this::doSomething);
}
private void doInTransaction(Runnable run) { ... }
}
When calling doSomethingInTransaction() the method doInTransaction is actually called with an Object of type Runnable. It would sometimes be nice to log the name and class of the method that was passed here (that is, Foo.doSomething), as well as the object. Is it somehow possible to find out what that was via reflection or whatnot? If that requires specific Java versions, that'd be an interesting answer, too.
(UPDATE: please note that this is not a duplicate of the related question Java 8 - how to access object and method encapsulated as lambda since I'm mostly asking for the method that is encapsulated there. That wasn't asked there.)
The following example shows how to get the method reference name from the runnable. As explained in the comments, the code may be unnecesserarily complex and only works for certain cases (including the one in the question). Also, it makes certain assumptions that don't work in the general case.
Example class:
public class Test {
public void callingMethod() {
this.acceptingMethod(this::methodReferenceMethod);
}
public void acceptingMethod(final Runnable runnable) {
final String name = Util.getRunnableName(runnable, "acceptingMethod");
System.out.println("Name is " + name);
}
public void methodReferenceMethod() {
}
public static void main(final String[] args) {
new Test().callingMethod();
}
}
Now the actual magic here:
class Util {
public static String getRunnableName(final Runnable runnable, final String calledMethodName) {
final String callSiteMethodName = getCallSiteMethodNameNotThreadSafe();
final Class<?> callSiteClass = getDeclaringClass(runnable);
final String runnableName = extractRunnableName(callSiteClass, callSiteMethodName, calledMethodName);
return runnableName;
}
private static String extractRunnableName(
final Class<?> callSiteClass,
final String callSiteMethodName,
final String calledMethodName) {
try {
final AtomicReference<String> result = new AtomicReference<>(null);
final ClassReader cr = new ClassReader(callSiteClass.getName());
final TraceClassVisitor traceVisitor = new TraceClassVisitor(new PrintWriter(System.out));
cr.accept(new CheckClassAdapter(Opcodes.ASM7, traceVisitor, false) {
#Override
public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
if (!name.equals(callSiteMethodName)) {
return super.visitMethod(access, calledMethodName, descriptor, signature, exceptions);
}
return new CheckMethodAdapter(Opcodes.ASM7, super.visitMethod(access, name, descriptor, signature, exceptions), new HashMap<>()) {
#Override
public void visitInvokeDynamicInsn(final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) {
final String invokeDynamic = ((Handle) bootstrapMethodArguments[1]).getName();
result.set(invokeDynamic);
}
};
}
}, 0);
return result.get();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
public static String getCallSiteMethodNameNotThreadSafe() {
final int depth = 4;
return Thread.currentThread().getStackTrace()[depth].getMethodName();
}
public static Class<?> getDeclaringClass(final Runnable runnable) {
return Arrays.stream(runnable.getClass().getDeclaredFields())
.filter(f -> f.getName().equals("arg$1"))
.map(f -> {
f.setAccessible(true);
try {
return f.get(runnable).getClass();
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.findFirst()
.orElseThrow(IllegalStateException::new);
}
}
The output is as expected "Name is methodReferenceMethod". I would probably never use this in any project, but I guess it is possible. Also, this only works for the given example, as there is only one INVOKEVIRTUAL in the calling method. For the general case, one would need to adjust the checkMethodVisitor and filter the calls to the "calledMethodName" only. Lastly, the code to get the calling method uses a fixed index for the stack trace element, which also does not generalize well.

Why doesn't ASM call my ``visitCode``?

I'll add my code to the end of this post.
I'm using byteBuddy 1.7.9 and whatever ASM version comes with that.
In a nutshell
I have
byte[] rawClass = ...;
ClassReader cr = new ClassReader(rawClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodAdder ma = new MethodAdder(Opcodes.ASM5,cw);
cr.accept(ma,ClassReader.EXPAND_FRAMES);
Where in MethodAdder, I want to add a static initialiser:
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if(mv != null){
if(!name.equals(CLINIT_NAME)) return mv;
else{
hasStaticInitialiser = true;
return new ClinitReplacer(api,mv,classname);
}
}else return null;
}
hasStaticInitialiser = true is reached, but ClinitReplacer.visitCode is never executed.
Why?
the whole story
Let's say I want to generate class B from this example using byteBuddy.
Why bytebuddy? Well, for one it's supposedly convenient, and for another, I need its class reloading functionality.
But as you can see in the tutorial, there are some inconveniences with using "pure" byte buddy code. Most importantly,
if you really need to create byte code with jump instructions, make sure to add the correct stack map frames using ASM since Byte Buddy will not automatically include them for you.
I don't want to do that.
And even if I wanted to, I tried
builder = builder
.defineMethod("<clinit>",void.class, Modifier.STATIC)
.withParameters(new LinkedList<>())
.withoutCode()
;
and all it got me was an
Exception in thread "main" java.lang.IllegalStateException: Illegal explicit declaration of a type initializer by class B
at net.bytebuddy.dynamic.scaffold.InstrumentedType$Default.validated(InstrumentedType.java:901)
at net.bytebuddy.dynamic.scaffold.MethodRegistry$Default.prepare(MethodRegistry.java:465)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:162)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2639)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2741)
at Main.main(Main.java)
So what I do instead is, I stop after I've added all my fields, get the bytecode for that and load the class.
Then I have ASM add the methods for me. ( In the actual application, I also need to run the bytecode through some other ASM visitors anyway.)
And then reload the re-instrumented bytecode using ByteBuddy.
The reloading fails with
Exception in thread "main" java.lang.ClassFormatError
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:261)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:171)
at Main.main(Main.java)
And the reason for that seems to be that B looks like this when disassembled:
super public class B
extends A
version 51:0
{
public static final Field foo:"Ljava/util/Set;";
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method A."<init>":"()V";
return;
}
static Method "<clinit>":"()V";
} // end Class B
Comparing that to the rawClass bytecode, we notice that
static Method "<clinit>":"()V";
didn't exist and was indeed added by the MethodAdder.
However, the Visitor returned in
return new ClinitReplacer(api,mv,classname);
is never used. And therefore the static initialiser body remains empty resulting in the wrongful classification as native.
Code
A.java
import java.util.HashSet;
import java.util.Set;
public class A{
public static final Set foo;
static{
foo = new HashSet<String>();
foo.add("A");
}
}
Main.java
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.jar.asm.*;
import net.bytebuddy.jar.asm.commons.InstructionAdapter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
ByteBuddyAgent.install();
String targetClassname = "B";
Class superclass = A.class;
ByteBuddy byteBuddy = new ByteBuddy();
DynamicType.Builder builder = byteBuddy
.subclass(superclass)
.name(targetClassname)
;
for(Field f : superclass.getFields()){
builder = builder.defineField(f.getName(),f.getType(),f.getModifiers());
}
DynamicType.Unloaded<?> loadable = builder.make();
byte[] rawClass = loadable.getBytes();
loadable.load(A.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
ClassReader cr = new ClassReader(rawClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodAdder ma = new MethodAdder(Opcodes.ASM5,cw);
cr.accept(ma,ClassReader.EXPAND_FRAMES);
byte[] finishedClass = cw.toByteArray();
Class unfinishedClass;
try {
unfinishedClass = Class.forName(targetClassname);
}catch(ClassNotFoundException e){
throw new RuntimeException(e);
}
ClassReloadingStrategy.fromInstalledAgent()
.load(
A.class.getClassLoader(),
Collections.singletonMap((TypeDescription)new TypeDescription.ForLoadedType(unfinishedClass), finishedClass)
);
Set<String> result;
try {
result = (Set<String>)Class.forName("B").getField("foo").get(null);
}catch(ClassNotFoundException | NoSuchFieldException | IllegalAccessException e){
throw new RuntimeException(e);
}
System.out.println(result);
}
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
FileChannel fc = null;
try {
Files.deleteIfExists(path);
fc = new FileOutputStream(path.toFile()).getChannel();
fc.write(ByteBuffer.wrap(finishedClass));
} finally {
if (fc != null) {
fc.close();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static class MethodAdder extends ClassVisitor implements Opcodes{
private static final String INIT_NAME = "<init>";
private static final String INIT_DESC = "()V";
private static final int CLINIT_ACCESS = ACC_STATIC;
private static final String CLINIT_NAME = "<clinit>";
private static final String CLINIT_DESC = "()V";
private static final String CLINIT_SIG = null;
private static final String[] CLINIT_EXCEPT = null;
public MethodAdder(int api, ClassVisitor cv) {
super(api, cv);
}
private String classname = null;
private boolean hasStaticInitialiser = false;
#Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classname = name;
hasStaticInitialiser = false;
cv.visit(version, access, name, signature, superName, interfaces);
}
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if(mv != null){
if(!name.equals(CLINIT_NAME)) return mv;
else{
hasStaticInitialiser = true;
return new ClinitReplacer(api,mv,classname);
}
}else return null;
}
#Override
public void visitEnd() {
if(!hasStaticInitialiser) visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
private static class ClinitReplacer extends InstructionAdapter implements Opcodes{
private final String classname;
public ClinitReplacer(int api, MethodVisitor mv, String classname) {
super(api, mv);
this.classname = classname;
}
#Override
public void visitCode() {
mv.visitCode();
InstructionAdapter mv = new InstructionAdapter(this.mv);
mv.anew(Type.getType(HashSet.class));
mv.dup();
mv.dup();
mv.invokespecial(Type.getInternalName(HashSet.class),INIT_NAME,INIT_DESC,false);
mv.putstatic(classname,"foo",Type.getDescriptor(Set.class));
mv.visitLdcInsn(classname);
mv.invokevirtual(Type.getInternalName(HashSet.class),"add","(Ljava/lang/Object;)Z",false);
mv.visitInsn(RETURN);
}
}
}
}
The problem is that your source class file doesn’t have a <clinit> method, hence, ASM doesn’t invoke visitMethod at all; it is you who does in
#Override
public void visitEnd() {
if(!hasStaticInitialiser) visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
Here, you are invoking visitMethod for <clinit> if you didn’t encounter it before, but you’re not doing anything with the returned MethodVisitor, hence, no-one is doing anything with it.
If you want to treat an absent <clinit> like visiting an empty initializer, to be transformed, you have to perform the appropriate method calls yourself, i.e.
#Override
public void visitEnd() {
if(!hasStaticInitialiser) {
MethodVisitor mv = visitMethod(CLINIT_ACCESS,CLINIT_NAME,CLINIT_DESC,CLINIT_SIG,CLINIT_EXCEPT);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
if(!hasStaticInitialiser) throw new IllegalStateException("ClinitReplacer not created");
super.visitEnd();
}
But note that then, you can’t do hot code replacement, as it doesn’t support adding any methods, including <clinit>. Further, hot code replacement won’t (re-)execute class initializers anyway.
But in your code, there is no need to load the type before performing your ASM transformation. You may remove the line
loadable.load(A.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
and then just use the resulting finishedClass bytecode, e.g.
ClassLoadingStrategy.Default.INJECTION.load(A.class.getClassLoader(),
Collections.singletonMap(loadable.getTypeDescription(), finishedClass));
Note that you won’t see much effect, as you are injecting code creating a HashMap, but not doing anything useful with it. You likely want to assign it to a field…
And, by the way, your code for writing a byte array is unnecessarily complicated:
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
FileChannel fc = null;
try {
Files.deleteIfExists(path);
fc = new FileOutputStream(path.toFile()).getChannel();
fc.write(ByteBuffer.wrap(finishedClass));
} finally {
if (fc != null) {
fc.close();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Just use
private static void store(String name, byte[] finishedClass) {
Path path = Paths.get(name + ".class");
try {
Files.write(path, finishedClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Both, “create if it doesn’t exist” and “overwrite/truncate if it exists” are the default behavior.
To answer the part about defining a type initializer in Byte Buddy, this can be done using:
builder = builder.invokable(isTypeInitializer()).intercept(...);
You cannot explicitly define a type initializer as those initializers are for example never exposed by the reflection API and this helps to keep Byte Buddy's type description model coherent. Instead, you match the type initializer and Byte Buddy makes sure that an intializer is added as it seems appropriate.

help with reflection + constructors

I have code I'm working on to instantiate a CRC algorithm dependent on a polynomial passed in, and a string s that contains "crc8" or "crc16" or "crc32".
The classes CRC8, CRC16, and CRC32 all extend a class CRC and implement an interface HashAlgorithm. Each of them has a constructor CRCx(int polynomial).
My problem is, I get this error on all 3 of the getConstructor() lines:
Type mismatch:
cannot convert from Constructor<HashFactory.CRC16>
to Constructor<HashFactory.CRC>
Can anyone help explain why and help me fix this?
int polynomial; // assign from somewhere
Constructor<CRC> crc = null;
if ("crc8".equals(s))
{
crc = CRC8.class.getConstructor(Integer.TYPE);
}
if ("crc16".equals(s))
{
crc = CRC16.class.getConstructor(Integer.TYPE);
}
if ("crc32".equals(s))
{
crc = CRC32.class.getConstructor(Integer.TYPE);
}
if (crc != null)
{
CRC crcInstance = crc.newInstance(polynomial);
return (HashAlgorithm) crcInstance;
}
Try
int polynomial; // assign from somewhere
if ("crc8".equals(s)) {
return new CRC8(polynomial);
} else
if ("crc16".equals(s)) {
return new CRC16(polynomial);
} else
if ("crc32".equals(s)) {
return new CRC32(polynomial);
}
Or
package tests;
import java.lang.reflect.Constructor;
public class Construct {
static interface CRC { }
static class CRC8 implements CRC {
public CRC8(int p) { }
}
static class CRC16 implements CRC {
public CRC16(int p) { }
}
static class CRC32 implements CRC {
public CRC32(int p) { }
}
public static CRC getAlg(String s, int polynomial) {
try {
Class<?> clazz = Class.forName("tests.Construct$" + s.toUpperCase());
Constructor<?> c = clazz.getConstructor(Integer.TYPE);
return CRC.class.cast(c.newInstance(polynomial));
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
throw new AssertionError("Unknown algorithm: " +s);
}
public static void main(String[] args) throws Exception {
System.out.println(getAlg("crc8", 0));
System.out.println(getAlg("crc16", 0));
System.out.println(getAlg("crc32", 0));
System.out.println(getAlg("crc48", 0));
}
}
"Factory" pattern:
public static HashAlgorithm getHashAlgorithm(String s, int polynomial) {
if ("crc8".equals(s)) {
return new CRC8(polynomial);
} else
if ("crc16".equals(s)) {
return new CRC16(polynomial);
} else
if ("crc32".equals(s)) {
return new CRC32(polynomial);
}
throw new AssertionError("Unknown algorithm: " +s);
}
It can be done several other ways (e.g HashMap of algorithms to duplicatable classes of CRCs, etc.)
Try declaring the crc variable like this:
Constructor<? extends CRC> crc = null;
Thanks to the wonders of generics, Constructor<HashFactory.CRC16> is not type compatible with Constructor<HashFactory.CRC>. You need to pick something more general for your variable, like this:
Constructor<? extends CRC> crc = null;
Others have offered solutions to your problem, but my advice is to not use Java reflection where you don't need to. A solution that uses reflection is typically more slower, the code is more complex, and there typically are more "dynamic typing" failure cases to consider.
In your particular example, the "object factory" pattern is a better solution than using reflection to invoke constructors.

Categories