I pass an Object Type variable inside a method and i want to cast it to a specific class according to a flag (I am also passing the flag).
If I put the code inside the if - else statement it works, but i end up with duplicate code.
This is an existing project and I cannot mess with the objects.
public void insertReport(Object request , String requestJson , int reportFlag){
Object reportRequest;
if (reportFlag == 0 ) {
reportRequest = (MonthlyCls) request;
}else{
reportRequest = (DailyCls) request;
}
RepEntity repEntity = new RepEntity ();
repEntity.setId(reportRequest.getReportInfo().getId());
repEntity.setDate(newTimestamp(reportRequest.getReportInfo().getDate()));
Is there a way to make java "understands" the casting in compile time?
Thank you very much in advance.
Simply use an interface or an abstract class to have a common type.
public abstract class Request{
public abstract ReportingInfo getReportInfo();
}
Then Extends the class in both classes, the methods should already be implemented.
public class DailyCls extends Request {
public ReportingInfo(){ ... }
}
public class MonthlyCls extends Request {
public ReportingInfo(){ ... }
}
You can also implement the method in Request if it is possible/necessary.
That way, you just have to change the signature to accept a Request
public void insertReport(Request request, String requestJson){
RepEntity repEntity = new RepEntity ();
repEntity.setId(request.getReportInfo().getId());
repEntity.setDate(newTimestamp(request.getReportInfo().getDate()));
}
First thing first, even if you cast the object to either MonthlyCls or DailyCls, the variable reportRequest is of type Object, so casting that object will not do anything. In order to be able to "access" the specific methods of both classes, you need to write something like this:
public void insertReport(Object request , String requestJson , int reportFlag){
MonthlyCls reportRequestMonthly = null;
DailyCls reportRequestDaily = null;
if (reportFlag == 0 ) {
reportRequestMonthly = (MonthlyCls) request;
}else{
reportRequestDaily = (DailyCls) request;
}
RepEntity repEntity = new RepEntity ();
if (reportRequestMonthly != null){
repEntity.setId(reportRequestMonthly .getReportInfo().getId());
repEntity.setDate(new Timestamp(reportRequestMonthly .getReportInfo().getDate()));
} else {
repEntity.setId(reportRequestDaily .getReportInfo().getId());
repEntity.setDate(new Timestamp(reportRequestDaily .getReportInfo().getDate()));
}
EDIT: I am assuming that both the objects are not related in any way by a SuperClass or anything, I suggest you do so and you also check with instanceof if the object given is correct.
If you can update the classes existing, a solution would be to implement an adapter that would use the reflection to call the methods.
Something like this would be quite safe to use
class RequestAdapter{
private Object request;
public RequestAdapter(Object request){
if(request == null) throw new IllegalArgumentException("The request can't be null");
if(!isSupported(request)) throw new IllegalArgumentException("Type not supported : " + request.getClass().getName());
this.request = request;
}
// call the method "getReportingInfo" by reflection on the object
public ReportingInfo getReportingInfo(){
try {
return (ReportingInfo) request.getClass().getMethod("getReportingInfo").invoke(request);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e) {
e.printStackTrace();
return null;
}
}
static Class<?>[] supportedType = {
DailyCls.class,
MonthlyCls.class
};
//Check if the type is supported, to prevent any mistake with the reflection call later.
private boolean isSupported(Object request){
for(Class<?> c : supportedType){
if(c == request.getClass()){
return true;
}
}
return false;
}
}
This is really not a clean solution, but if the method public ReportingInfo getReportingInfo() is defined in every class you add in the supportedType array. This would be quite safe.
Tested with :
public void insertReport(Object request){
RepEntity repEntity = new RepEntity ();
RequestAdapter adapter = new RequestAdapter(request);
repEntity.setId(adapter.getReportInfo().getId());
repEntity.setDate(newTimestamp(adapter.getReportInfo().getDate()));
}
If anything else than a MonthlyCls or DailyCls is pass to the method, you will get an IllegalArgumentException :
Like this : new RequestAdapter("");
Exception in thread "main" java.lang.IllegalArgumentException: Type not supported : java.lang.String
Having a dynamic proxy for an interface with default methods, how do I invoke a default method? By using something like defaultmethod.invoke(this, ...) you just get your proxy invocation handler called (Which is somehow correct, cause you have no implementing class for this interface).
I have a workaround using ASM to create a class implementing the interface and delegating such calls to an instance of this class. But this is not a good solution, especially if the default method calls other interface methods (you get a delegator ping-pong). The JLS is surprisingly silent about this question...
Here a small code example:
public class Java8Proxy implements InvocationHandler {
public interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
#Test
public void invokeTest() {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, this);
proxy.someDefaultMethod();
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// assuming not knowing the interface before runtime (I wouldn't use a
// proxy, would I?)
// what to do here to get the line printed out?
// This is just a loop
// method.invoke(this, args);
return null;
}
}
You can use the MethodHandles type in your InvocationHandler. This code is copied from Zero Turnaround.
Constructor<MethodHandles.Lookup> constructor;
Class<?> declaringClass;
Object result;
if (method.isDefault()) {
declaringClass = method.getDeclaringClass();
constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
result = constructor.
newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
unreflectSpecial(method, declaringClass).
bindTo(proxy).
invokeWithArguments(args);
return(result);
}
The accepted answer uses setAccessible(true) to break into MethodHandles.Lookup, something that is restricted in Java 9 and beyond. This mail describes a JDK change that works for Java 9 or later.
It is possible to get this to work on Java 8 (and later) if you can get the writer of the interface to call your utility with an instance of MethodHandles.Lookup created in the interface (so it gets the permission to access the default methods of the interface):
interface HelloGenerator {
public static HelloGenerator createProxy() {
// create MethodHandles.Lookup here to get access to the default methods
return Utils.createProxy(MethodHandles.lookup(), HelloGenerator.class);
}
abstract String name();
default void sayHello() {
System.out.println("Hello " + name());
}
}
public class Utils {
static <P> P createProxy(MethodHandles.Lookup lookup, Class<P> type) {
InvocationHandler handler = (proxy, method, args) -> {
if (method.isDefault()) {
// can use unreflectSpecial here, but only because MethodHandles.Lookup
// instance was created in the interface and passed through
return lookup
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(proxy)
.invokeWithArguments(args);
}
return ...; // your desired proxy behaviour
};
Object proxy = Proxy.newProxyInstance(
type.getClassLoader(), new Class<?>[] {type}, handler);
return type.cast(proxy);
}
}
This approach won't handle all Java 8 use cases, but it did handle mine.
Since jdk-16 this is supported in a native way, via invokeDefault.
To your example, this would be done as:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, new Java8Proxy());
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
static class Java8Proxy implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoked");
InvocationHandler.invokeDefault(proxy, method, args);
return null;
}
}
}
But you do not need an explicit implementation of the interface that you need, this can be done slightly different:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class },
(o, m, params) -> {
if (m.isDefault()) {
// if it's a default method, invoke it
return InvocationHandler.invokeDefault(o, m, params);
}
return null;
});
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
}
I wrote up a blog entry detailing the different approaches that must be used for Java 8 and 9+: http://netomi.github.io/2020/04/17/default-methods.html
It includes code from the spring framework to handle the different cases in a clean and efficient way.
This is annoyingly stupid counter-intuitive behaviour, which I assert is a bug in method#invoke(Object,Object[]), because you can't keep things simple in an InvocationHandler, like:
if (method.isDefault())
method.invoke(proxy, args);
else
method.invoke(target, args); // to call a wrapped object
So have to do a special lookup for a MethodHandle, and bind to proxy, to call, it.
I refined the McDowell provided code as follows (simplified):
private static final Constructor<MethodHandles.Lookup> lookupConstructor;
static {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private static MethodHandle findDefaultMethodHandle(Class<?> facadeInterface, Method m) {
try {
Class<?> declaringClass = m.getDeclaringClass();
// Used mode -1 = TRUST, because Modifier.PRIVATE failed for me in Java 8.
MethodHandles.Lookup lookup = lookupConstructor.newInstance(declaringClass, -1);
try {
return lookup.findSpecial(facadeInterface, m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes()), declaringClass);
} catch (IllegalAccessException e) {
try {
return lookup.unreflectSpecial(m, declaringClass);
} catch (IllegalAccessException x) {
x.addSuppressed(e);
throw x;
}
}
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class InvocationHandlerImpl implements InvocationHandler {
private final Class<?> facadeInterface;
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
MethodHandle mh = findDefaultMethodHandle(facadeInterface, m);
return mh.bindTo(proxy).invokeWithArguments(args);
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// rest of code method calls
}
}
facadeInterface is the interface being proxied, which declares the default method, it will probably be possible to use super-interface default methods too.
Non-toy code should do this lookup before invoke is called, or at least cache the MethodHandle.
I want to get my class property with string name.
I have a code like this
class Test
{
public String simple = "hello";
public void getSetting()
{
try
{
Test c = new Test();
Class cls = this.getClass();
Field field = cls.getField("simple");;
}
catch(Exception e)
{
// error
}
}
}
I get an error with this code , because my property is non-static , and when i changing my property to static , it's work fine , how can i get non-static properties with reflection?
Here's a self-contained example on how to get an instance Field through reflection.
public class Main {
// the instance field
String simple = "foo";
// some static main method
public static void main(String[] args) throws Exception {
// initializing the class as we're accessing an instance method
new Main().reflect();
}
public void reflect() {
Class<?> c = this.getClass();
try {
// using getDeclaredField for package-protected / private fields
Field field = c.getDeclaredField("simple");
// printing out field's value for this instance
System.out.println(field.get(this));
}
// TODO handle better
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (NoSuchFieldException n) {
n.printStackTrace();
}
}
}
Output
foo
try
{
Test c = new Test();
Class cls = c.getClass(); //Change this.getClass to c.getClass()
Field field = cls.getField("simple");;
}
The Field must be static or belong to a instance that can be get via reflection.
[EDIT: I've rewritten the code to further simplify it and focus on the issue at hand]
I'm working on this particular piece of code:
class SimpleFactory {
public SimpleFactory build() {return null}
}
class SimpleFactoryBuilder {
public Object build(final Class builderClazz) {
return new SimpleFactory() {
#Override
public SimpleFactory build() {
return new builderClazz.newInstance();
}
};
}
}
However, the builder in the return statement triggers the error "Cannot find symbol newInstance". It's as if builderClazz wasn't recognized as a class object.
How can I make it work?
EDIT: SOLUTION (thanks to dcharms!)
The code above is a partial simplification of the code I was dealing with. The code below is still simplified but includes all the components involved and includes the solution provided by dcharms.
package com.example.tests;
interface IProduct {};
interface ISimpleFactory {
public IProduct makeProduct();
}
class ProductImpl implements IProduct {
}
class SimpleFactoryBuilder {
public ISimpleFactory buildFactory(final Class productMakerClazz) {
return new ISimpleFactory() {
#Override
public IProduct makeProduct() {
try {
// the following line works: thanks dcharms!
return (IProduct) productMakerClazz.getConstructors()[0].newInstance();
// the following line -does not- work.
// return new productMakerClazz.newInstance();
}
catch (Exception e) {
// simplified error handling: getConstructors() and newInstance() can throw 5 types of exceptions!
return null;
}
}
};
}
}
public class Main {
public static void main(String[] args) {
SimpleFactoryBuilder sfb = new SimpleFactoryBuilder();
ISimpleFactory sf = sfb.buildFactory(ProductImpl.class);
IProduct product = sf.makeProduct();
}
}
You cannot instantiate a new object this way. builder is a Class object. Try instead the following:
return builder.getConstructors()[0].newInstance(anInput);
Note: this assumes you are using the first constructor. You may be able to use getConstructor() but I'm not sure how it would behave with the generic type.
I'm just wondering if it's possible to invoke a method by reflection inside a thread. My idea is to pass to the thread constructor a method name so that is how I would like to specify what the thread should do (which method should be run). The code below is not working - I obtain an error "ClassNotFoundException".
public class Listener extends Thread {
/** Constructor */
private static Window win = new Window();
private static Class c;
private String parameter;
public Listener(String param) {
this.parameter = param;
}
public void run() {
try {
Class c = Class.forName("Listener");
Class partypes[] = new Class[1];
partypes[0] = String.class;
Method meth = c.getMethod("waitForWindowAppear", partypes);
Listener methobj = new Listener(parameter);
Object arglist[] = new Object[1];
arglist[0] = parameter;
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
System.out.println(retval.intValue());
} catch (Exception e) {
e.printStackTrace();
}
}
/** Method waits as a thread for window at class or title */
private static void waitForWindowAppear(String title) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException exc) {
System.out.println("Thread waitForWindowAppear has been stoped!");
return;
}
if (win.isWindowVisible(title)) {
// System.out.println("Window found!");
return;
} else {
// System.out.println("Waiting for window!");
}
}
}
}
Do anyone have some ideas how to solve this problem?
I would say you don't have this class Listener on your Default package, therefore, what you should do is use the fully qualified name, for example:
Class c = Class.forName("foo.bar.Listener");
The ClassNotFoundException is thrown because it doesn't find Listener class, and one reason would be the incomplete name.
Additionally, you use getMethod, this will not work with private methods, please use getDeclaredMethod instead.
From documentation:
getMethod - Returns a Method object that reflects the specified public
member method of the class or interface represented by this Class
object.
getDeclaredMethod - Returns a Method object that reflects the
specified declared method of the class or interface represented by
this Class object.