Let's say we have this class:
public abstract class A {
int x;
public int foo();
public int foo(int x);
}
As we all know, A.class
has a distinct advantage over Class.forName("A")
: it will still work if the name of A
is changed by refactoring or obfuscation.
However, there is no way to get this advantage with fields and methods. Have you ever wished that you could do this: (see a better proposed syntax in the edit below!)
Field xField = A.x.field;
Method fooMethod = A.foo().method;
Method fooIntMethod = A.foo(int).method;
Instead of this?
Field xField = A.getField("x");
Method fooMethod = A.getMethod("foo");
Method fooIntMethod = A.getMethod("foo", int.class);
So here's my question: does anyone know if this feature has been planned or discussed or if Sun/Oracle specifically decided against it for some reason?
EDIT: How about this syntax? It avoids problems people have mentioned:
Field xField = A..x;
Method fooMethod = A..foo();
Method fooIntMethod = A..foo(int);
Example use case
I recently created an AbstractTableModel
class called EasyTableModel
that allows you to define your own POJO row type. Its getValueAt(...)
and setValueAt(...)
etc. use reflection to get/set the values of the fields in the POJO.
public class EasyTableModel<T> extends AbstractTableModel {
private RowFormat<T> prototypeFormat;
private final ArrayList<T> rows = new ArrayList<T>();
...
public static interface RowFormat<T> {
Object getValueAt(T row, int columnIndex);
void setValueAt(T row, Object value, int columnIndex);
...
}
...
public static class ReflectionRowFormat<T> implements RowFormat<T> {
private Field[] fields;
...
public Object getValueAt(T row, int column) {
try {
return fields[column].get(row);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setValueAt(T row, Object value, Field field) {
if (!field.getDeclaringClass().isInstance(this)) {
throw new IllegalArgumentException("field is not a member of this class");
}
setValueAt(row, value, getColumnIndex(field));
}
...
}
...
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return getRowFormat(rowIndex).getValueAt(rows.get(rowIndex), columnIndex);
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
getRowFormat(rowIndex).setValueAt(rows.get(rowIndex), aValue, columnIndex);
fireTableRowsUpdated(rowIndex, rowIndex);
}
public void fireTableCellUpdated(T row, String columnName) {
fireTableCellUpdated(row, indexOfColumn(columnName));
}
public void fireTableCellUpdated(T row, Field field) {
fireTableCellUpdated(row, indexOfColumn(field));
}
}
Using this base class, it's extremely easy to create your table:
public abstract class QuoteMonitorTableModel<R extends QuoteMonitorTableModel<R>.Row> extends EasyTableModel<R> {
...
protected static final String NUM_QUOTES_RECEIVED = "# Quotes Received";
protected static final String LAST_QUOTE_TIME = "Last Quote Time";
public class Row {
public Row() {
}
@ColumnName(NUM_QUOTES_RECEIVED)
private Integer numQuotesReceived;
@ColumnName(LAST_QUOTE_TIME)
private Long lastQuoteTimeMillis;
public Integer getNumQuotesReceived() {
return numQuotesReceived;
}
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
fireTableCellUpdated((R) this, NUM_QUOTES_RECEIVED);
}
public Long getLastQuoteTimeMillis() {
return lastQuoteTimeMillis;
}
public void setLastQuoteTimeMillis(Long lastQuoteTimeMillis) {
this.lastQuoteTimeMillis = lastQuoteTimeMillis;
fireTableCellUpdated((R) this, LAST_QUOTE_TIME);
}
}
}
What are the advantages of all this?
getValueAt(...)
and setValueAt(...)
If you think this is an abuse of reflection, then you would consider many well-used libraries like Google GSON as an abuse of reflection as well.
Now, notice how the derived class indicates which field has changed when firing events by a String, rather than the Field:
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
fireTableCellUpdated((R) this, NUM_QUOTES_RECEIVED);
}
It would be nice if we could just use the fields. But doing it with getDeclaredField() would suck:
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
try {
// what if obfuscation changes the name of the numQuotesReceived field?
fireTableCellUpdated((R) this, getClass().getDeclaredField("numQuotesReceived"));
} catch (NoSuchFieldException e) {
}
}
However, with the feature I am proposing, it would be easy as cake:
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
// if obfuscation changes the name of the numQuotesReceived it will
// not break the compiled form of this code
fireTableCellUpdated((R) this, QuoteMonitorTableModel..numQuotesReceived);
}
If you think this feature wouldn't open up a world of possibilities for useful programming tools, you're lacking imagination ;)
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
In order to reflect a Java class, we first need to create an object of Class . And, using the object we can call various methods to get information about methods, fields, and constructors present in a class. class Dog {...} // create object of Class // to reflect the Dog class Class a = Class.
Yes, you can use reserved words.
It would be difficult to introduce keywords in Java, however you can introduce new symbol combinations. For example in Java 8 you can write
MyClass::myMethod
To get a Method Reference, something you can't do in Java 7.
You could do something similar with fields and there have been suggestions to support property references.
You can even write
HashSet<String>::new
to get a reference to a constructor ALA new HashSet<String>()
You can turn a Method into a MethodHandle with MethodHandles.Lookup.unreflect(Method) and you can turn a Constructor into a MethodHandle with MethodHandles.Lookup.unreflectConstructor(Constructor)
Once you have a MethodHandle you can set an object for it to call on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With