How to calculate bridge method target - java

Generics bring in a lot of goodness to java but they also mean trouble because of the introduction of bridge methods which mean its not easy to locate a method given a name and target because erasure and bridge methods mean clients actually use the bridge rather than the target.
Return types from methods have been omitted because they are not important for this quetion...
class Super{
}
class Sub extends Super{
}
interface Interface<T extends Super>{
method( T t ); // erased -> method( Super super );
}
interface Interface2 extends Interface<T extends Sub>{
method2( T t ); // erased -> method2( Sub sub );
}
class Concrete implements Interface2{
method( Sub sub ); // erased to method( Super super);
method2( Sub sub );
}
how can i programmatically determine that Concrete.method(Super) ends up calling Concrete.method(Sub) ? I would like to avoid calculations based on parameterised types and what not as this gets complicated fast...
I have looked at Springs BridgeMethodResolver but its quite complex and does a lot of stuff, surely theres an easier way.. perhaps theres not..

What came to my mind is:
find all possible variants of method arguments, (using .getSuperclass()) with the final one being (Object, Object, ...)
loop through all these and find the one that isnt Method.isBridge();
But this is again too much work, and it might not work :). I'd propose doing just:
Method target = new BridgeMethodResolver().findBridgedMethod(bridgedMethod);
No matter what the complexity is there, it's hidden from you.
If you don't want dependecy on spring, just copy-paste the code of that class.

I faced a related problem - I needed to retrieve annotations of the "original" method, and found it very hard to find out the method because of the bridges (which don't have the annotations). I ended up submitting an enhancement request which is accepted. In your case I don't know what you need the method for, so unfortunately, the request (as it is now formulated) may not help you.

Related

Using 'diamond' notation for methods in java

I'm currently working on a component-based architecture management system in java. My current implementation of the retrieval of a component attached to an object works like this:
// ...
private final HashMap<Class<? extends EntityComponent>, EntityComponent> components;
// ...
public <T extends EntityComponent> T getComponent(Class<T> component)
{
// ... some sanity checks
if (!this.hasComponent(component))
{
// ... some exception handling stuff
}
return component.cast(this.components.get(component));
}
// ...
Now, this works fine, but it somewhat bugs me to have to write
object.getComponent(SomeComponent.class)
everytime I need to access a component.
Would it be possible to utilize generics in a way to shift the syntax to something more along the lines of
object.getComponent<SomeComponent>()
, utilizing the diamond operator to specify the class, instead of passing the class of the component as a parameter to the method?
I know it's not really a big thing, but making the syntax of often used code as pretty / compact as possible goes a long way I guess.
Unfortunately not, since type-parameters are "erased" in Java. That means that they are only available at compile-time (where the compiler is using them to type-check the code), but not at run-time.
So when your code is running, the <SomeComponent> type-parameter no longer exists, and your code therefore can't do any operations (if/else, etc) based on its value.
In other words:
At compile time, your method call looks like this: object.getComponent<SomeComponent>()
But after compilation your method call just looks like this object.getComponent(). There is no type-parameter any more.
So, yes, unfortunately you still need to pass a Class object along, or something similar (see "Super Type Tokens" for example), if you need to do something that depends on the type parameter at run-time.
The reason the Class workaround works is that it loosely speaking represents the type-parameter, since the type-checker makes sure that its instance fits with the type-parameter, but is an object and thus available at run-time too - unlike the type-parameter.
Note: The Class-trick doesn't work for type-parameters within type-parameters, such as Class<List<Something>>, since at run-time List<Something> and List<OtherThing> is the same class, namely List. So you can't make a Class token to differentiate between those two types. As far as i remember "Super Type Tokens" can be used instead to fix this (they exploit the fact that there is an exception to erasure: For subclasses of generic classes, the type-parameters used when "extending" the superclass are actually available at run-time through reflection. (there are also more exceptions: https://stackoverflow.com/a/2320725/1743225)).
(Related google terms: "Erasure", "Reification", "Reified generics")

How can we access methods generated by ByteBuddy in compilation time?

I wrote this example:
E someCreateMethod(Class<E> clazz) {
Class<? extends E> dynamicType = new ByteBuddy()
.subclass(clazz)
.name("NewEntity")
.method(named("getNumber"))
.intercept(FixedValue.value(100))
.defineField("stringVal", String.class, Visibility.PRIVATE)
.defineMethod("getStringVal", String.class, Visibility.PUBLIC)
.intercept(FieldAccessor.ofBeanProperty())
.make()
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
return dynamicType.newInstance();
}
And I would like to use it to get the redefined number atributte:
Integer num = someCreateMethod(EntityExample.class).getNumber(); //(1)
Or to get the newly defined stringVal attribute:
String sVal = someCreateMethod(EntityExample.class).getStringVal(); //(2)
My problem is that (1) works pretty fine, while (2) doesn't. I get the following error:
Error:(40, 67) java: cannot find symbol
symbol: method getStringVal()
Also, is it possible to do something like this with a dynamic generated class:
NewEntity newEntity = someCreateMethod(EntityExample.class);
Integer num = newEntity.getNumber();
String sVal = newEntity.getStringVal();
?
EDIT: I appreciate your help, this example was my first attempt on using ByteBuddy library. I figured that defineMethod actually defines an implementation of an interface method, not just add a random method to the class. So I decided to explain here what exactly I'm trying to accomplish.
For every Date attribute in a class E, I want to add two more fields (and theirs respectives getters and setters), let's say (atribute name)InitialDate and (atribute name)FinalDate, so that I can use intervals functinality for every date in E.
I was wondering if I could use code-generation to add those methods without having to create subclasses for every E.
PS: E can't be changed, it belongs to a legacy module.
PS2: I don't know how many date attributes there would be in each entity E, but the new attibutes and methods would be created using conventions (for example __FisrtDay , __LastDay), as shown below:
NewA a = eb.create(A.class);
a.getDeadLine(); //inherited
a.getDeadLineFirstDay(); //added
a.getDeadLineLastDay(); //added
NewA b = eb.create(B.class);
b.getBirthday(); //inherited
b.getBirthdayFirstDay(); //added
b.getBirthdayLastDay(); //added
b.getAnniversary(); //inherited
b.getAnniversaryFirstDay(); //added
b.getAnniversaryLastDay(); //added
PS3: Is what I'm trying to accomplish even possible with ByteBuddy or at all? Is there another way?
PS4: Should my EDIT have been a new question?
You need E to be a superclass/ or interface which includes the methods you are trying to call -- you will not be able to resolve subtyped methods which do not exist on E.
This is not a ByteBuddy issue, this is an issue of your class design -- you should design & group the functionality you intend to generate into abstractable parts, so it can be exposed via types which are meaningful at compile time.
For example, we could use a supertype 'ValueProvider' and then use ByteBuddy to define an IntConstantProvider.
public interface ValueProvider<T> {
public T getValue();
}
Class<? extends ValueProvider<Integer>> dynamicType = new ByteBuddy()
.subclass(clazz)
.name("ConstantIntProvider")
.method(named("getValue"))
.intercept(FixedValue.value(100))
// etc.
Your prototype had 3 separate functionalities (if we consider unreference private fields to be the stub of some intended behavior) with no obvious abstraction to encompass them. This could be better designed as 3 simple atomic behaviors, for which the abstractions would be obvious.
You could use reflection to find arbitrary methods on a arbitrary dynamically-defined class, but this is not really meaningful from a coding or design POV (how does your code know which methods to call? if it does know, why not use a type to express that?) nor is it very performant.
FOLLOWING EDIT TO QUESTION -- Java Bean properties work by reflection, so the example of finding "related properties" (such as First/ Last Date) from known properties is not unreasonable.
However it could be considered to use a DateInterval( FirstDate, LastDate) class so that only one supplementary property is needed per- base property.
As Thomas points out, Byte Buddy generates classes at runtime such that your compiler cannot validate their existance during compile time.
What you can do is to apply your code generation at build time. If your EntityExample.class exists in a specific module, you can enhance this module with the Byte Buddy Maven or Gradle plugin and then, after enhancement, allow your compiler to validate their existance.
What you can also do would be to define interfaces like
interface StringVal {
String getStringVal();
}
which you can ask Byte Buddy to implement in your subclass which allows your compiler to validate the method's existance if you represent your subclass as this interface.
Other than that, your compiler is doing exactly what it is supposed to do: telling you that you are calling a method that does not exist (at that time).

Out-of-nowhere ClassCastException in android

This morning I woke up on this strange exception:
java.lang.ClassCastException:
org.apache.harmony.luni.lang.reflect.ImplForVariable cannot be cast to
java.lang.Class
It happens when I try to get a Type argument Class.
Example:
public static Class getTypeParameterClass(Object o, int index) {
return (Class)
((ParameterizedType)o.getClass()
.getGenericSuperclass())
.getActualTypeArguments()[index];
}
As you can see, this method gets one of the type parameter for a given object's class.
It's been working for months now but today, it stopped.
Usage example:
Collection<Object> foo = new ArrayList<Object>();
Class<?> fooClass = Utils.getTypeParameterClass(foo, 0);
And it crashes.
Is there a curse on me for blaming java too many times;) ?
Thanks !
As you can see, this method gets one of the type parameter for a given object's class.
So what you are doing is specifically not allowed because of the generic type erasure. You are trying to investigate the generic parameter of your ArrayList but when the code is running, the specific generic type is no longer visible.
Your superclass hack (and it is a hack) would work if you were dealing with a superclass. It would work if you defined your ArrayList like:
Collection<Object> foo = new ArrayList<Object>() {
private static final long serialVersionUID = -594043150300376049L;
};
In this case foo is a class which extends ArraryList. For some reason with the superclass the generic type parameter is available.
So, first of all, let me address a huge "thank you" to Gray for his patience.
I finally figured what was wrong.
I was focusing on the wrong field.
So, as Gray points out, because of type erasure you can't get a type parameter that is not explicitly given at compile time.
I was so persuaded that the error was on a Collection field that I totally forgot about other fields.
So, because my data come from webservices, and because the JSON mapper can't directly get a list as root, I had to define models containing a Collection of objects, a date (for caching) and few other things related to paging.
So, because writing all these similar classes was a nonsense, I tried to generalize the process, like so:
public abstract class ModelList<T> {
[ paging and caching stuffs ]
#ForeignCollectionField
Collection<T> collection;
[ constructor, getters, setters ]
}
and then only have subclasses like:
#DatabaseTable(tableName = "category_list")
public class CategoryList extends ModelList<Category> {
}
So I could have less verbose classes, and so I could generalize the caching process using ObjectPersister from Robospice.
What I couldn't understand (because of a lack of knowledge), is that the parameter would not be found at run time !
What confused me even more is that the tests I ran to try to reproduce the bug, WERE producing the bug ! but they were wrong too.
So, note to self (and others that may face complete nonsense like I did):
don't lose faith in the language (it's not buggy, it's not buggy, it's not buggy ...)
don't blame the language (not too much)
always attach sources to jars and set breakpoints
At the end the solution was quite simple, just move the collection from the ModelList to the sublasses, and keep the getters and setters as abstract methods in ModelList (for the generic ObjectPersisters).
I think it all got a little too complicated, if someone knows a better solution for this case using ormlite/jackson/robospice I'd be glad to hear it.

Casting to a bounded type

I have a class hierarchy where cousins share very similar functionality. For example:
Node
Statement
FunctionCallStatement
Expression
FunctionCallExpression
FunctionCallStatement and FunctionCallExpression share a very similar API, but I cannot express that in pure class terms with a single-inheritance hierarchy. So, I've created an IsFunctionCall Interface which both of these implement. I can now declare a method which takes either a FunctionCallStatement or a FunctionCallExpression as follows:
void <T extends Node & IsFunctionCall> doSomething(T node) { ... }
This all works very nicely.
Unfortunately, I've now found myself faced with a rather awkward problem. I have a Node; I know dynamically that it must be either a FunctionCallStatement or a FunctionCallExpression; I need to pass that Node into the doSomething() method above. I cannot find a way to upcast it to an appropriate type.
Right now I'm using a chain of instanceof to determine which class the Node is and to cast it to the appropriate concrete type, but that's butt-ugly. The only other way I know to make this work is to make an IsNode interface and have everything that currently expects a Node expect an IsNode instead; this would allow me to declare a union interface that implements IsNode and IsFunctionCall and let me do away without the generics above. But that's a hell of a lot of work and is still pretty ugly.
Is there an alternative way to do this?
(Note: example above is a simplified version of my actual code.)
Update: I tried the following piece of evil:
#SuppressWarnings("unchecked")
private <S extends Node & IsFunctionCall> S castNode(Node node)
{
return (S) node;
}
and then:
doSomething(castNode(node));
I got some very strange error messages. It would appear that the type inference used to determine the S of castNode() will not match against the T in the declaration of doSomething(); it's using the concrete type only and setting S to Node. Which of course does not match doSomething()'s declared type. Very peculiar.
Update update:
This appears to be a close duplicate of How should I cast for Java generic with multiple bounds?. My situation is slightly different because my bounds include an object and an interface, while the one in the other question has two interfaces, but it's still applicable.
Looks like I need to go and reengineer my entire application. Sigh.
Any admin, feel free to close this as a duplicate...
I think the way out of this, although not exactly elegant, is to have a few overloads for doSomething:
void doSomething(FunctionCallStatement node) ...
void doSomething(FunctionCallExpression node) ...
You are using the interface to flag functionality, how about passing as argument a reference to the FunctionCallInterface which offers access to the function call abstraction?
doSomething won't have to know the actual implementation type as long as it can access the relevant information and call relevant methods on the implementation objects.
public class FunctionCallStatement extends Statement implements FunctionCallInterface {
}
void doSomething(FunctionCallInterface node) {
}

Work around not being able to use non-reifiable parameters with varargs

There are many questions about the issue of combining generics with varargs. This would require generic arrays which don't exist when actual code tries to instantiate them. Moreover, there's a good amount of documentation on the compiler-vagueness of warnings from varargs methods with non-reifiable parameters. Because of type erasure this creates potential heap pollution, hence the warning (in Java 6 at the caller). However, my question is not about these problems themselves. I think I understand that some things aren't possible. What I'd like to know is the way to elegantly workaround these problems in my complex case.
Links for related topics:
Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning? where some call this situation a "bad feature" bug.
http://docs.oracle.com/javase/tutorial/java/generics/non-reifiable-varargs-type.html
My case
I have a BookItemSearchAddTask that extends from the Android AsyncTask but somewhere along its inheritance hierarchy has been made generic, more abstract at higher levels:
At a higher level it's SearchAddTask, which contains the method start() to execute the task, called from a client that knows that it passes a BookItem product in.
public abstract class SearchAddTask<ProductToAdd extends Product & NamedProduct>
extends AddTask<ProductToAdd, ProductToAdd> {
public void start(ViewActivity context, ProductToAdd product) throws SpecificAddTaskDomainException, TaskExistsException, TaskUnavailableException {
super.start(context, product);
//more stuff ...
execute(product);
}
}
A level lower it's an ItemSearchAddTask. Here the method doInBackground is implemented, as required by the AsyncTask API. It can still use generics.
public abstract class ItemSearchAddTask extends SearchAddTask<I> {
public I doInBackground(I... params) {
I product = params[0];
//do stuff ...
return product;
}
}
Finally BookItemSearchAddTask is ItemSearchAddTask<BookItem>. A BookItem therefore is an Item, is a Product. The "I" is linked to the class in which this nested task class, ItemSearchAddTask, finds itself:
public abstract class ItemSearchAddWindow<I extends Item & ImageRepresentedProduct & NamedProduct> extends ViewActivity implements View.OnClickListener,
AdapterView.OnItemClickListener {}
The problem
Now, when I run this code I get the following error:
Caused by: java.lang.ClassCastException: [Lnet.lp.collectionista.domain.Product;
at net.lp.collectionista.ui.activities.items.book.ItemSearchAddWindow$ItemSearchAddTask.doInBackground(ItemSearchAddWindow.java:1)
Note the "[L".
I also get compile time warnings at "execute(product);": "Type safety: A generic array of ProductToAdd is created for a varargs parameter"
The cause
To my understanding, the JVM finds that in the doInBackground vararg it gets a Product[] passed in, rather than the Item[] (I[]) it expects. Apart from the generic arrays, which are hard to think about, I think what's going on is the case of the lion cage at http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html. Not by my code, but because the generated generic array of ProductToAdd (which basically extends Product) was created for the varargs param.
I checked that if I pass no argument to execute, that it works. Also using "execute((ProductToAdd[])new MusicCDItem[]{new MusicCDItem()});" worked mostly (don't ask, a MusicCDItem is an Item, just like a BookItem is).
A solution?
However in start() I can't know that I need to pass in a BookItem. I only know about "I". Since that is a generic, I can't create the generic array that is required to pass as the varargs param. I think what is complex about my case is the different levels of using generics, as well as the parallel hierarchies.
How do I work around this feature gap? I considered:
Removing generics. A bit drastic.
Holding on the varargs param everywhere in all the generics bits of code (i.e. change the method signature of start()), until we reach the client code, and only there do I pass one product element only, and that element is of a real type, BookItem. I will get the same warning there but the compiler will be able to generate the correct generic array.
Duplicating the AsyncTask code and changing doInBackground to not use a varargs param because I currently may not need one. I prefer not to do this so I get the benefits when AsyncTask is updated in the future.
Perhaps some reflection code in start(). Ugly.
Is there anything shorter and more local?
Either this, or there is some really stupid typo in my code.
You noted that you get an "Unchecked generic array" warning in SearchAddTask.start() when it calls execute(). However, the actual warning is slightly misleading. What it says is A generic array of ProductToAdd is created for a varargs parameter, but what it really means is, ProductToAdd is a type variable and at run-time I can't create an array of those, so I'll just have to use my best guess.
If you step through into execute() in the debugger, you'll see that the array that was created for the P... declaration is a Product[1] -- exactly what you'd expect from the class cast exception you got. At run time, this is the best the JVM can do, because it's the closest un-erased ancestor of ProductToAdd.
Unfortunately, in ItemSearchAddTask, the JVM's also done the best it can, which is convert the I... declaration into an Item[] (the closest un-erased ancestor of I); thus the ClassCastException when execute() tries to call doInBackground().
The least awful way I can think of offhand to get around this is to sidestep Java's type erasure by keeping ProductToAdd's concrete class around at run time and creating the args array (of the correct type) yourself:
abstract class SearchAddTask<ProductToAdd extends Product & NamedProduct>
extends AddTask<ProductToAdd, ProductToAdd> {
private final Class<ProductToAdd> productClass;
SearchAddTask(Class<ProductToAdd> productClass) {
this.productClass = productClass;
}
public void start(ViewActivity context, ProductToAdd product) {
super.start(context, product);
ProductToAdd[] argsArray = (ProductToAdd[]) Array.newInstance( productClass, 1 );
argsArray[0] = product;
execute( argsArray );
}
}
It does mean you have to ensure that BookItem.class gets passed in, probably when you create the AddWindow<BookItem>, but it keeps the ugliness contained.

Categories