假设我们有这个课程:
public abstract class A {
int x;
public int foo();
public int foo(int x);
}
众所周知,
A.class
与Class.forName("A")
相比具有明显的优势:如果通过重构或混淆来更改A
的名称,它将仍然有效。但是,无法通过字段和方法获得此优势。您是否曾经希望做到这一点:(请在下面的编辑中查看建议的更好语法!)
Field xField = A.x.field;
Method fooMethod = A.foo().method;
Method fooIntMethod = A.foo(int).method;
代替这个?
Field xField = A.getField("x");
Method fooMethod = A.getMethod("foo");
Method fooIntMethod = A.getMethod("foo", int.class);
所以这是我的问题:是否有人知道是否已经计划或讨论了此功能,或者Sun / Oracle是否出于某些原因特别决定拒绝使用此功能?
编辑:这种语法怎么样?它避免了人们提到的问题:
Field xField = A..x;
Method fooMethod = A..foo();
Method fooIntMethod = A..foo(int);
示例用例
我最近创建了一个名为
AbstractTableModel
的EasyTableModel
类,该类允许您定义自己的POJO行类型。其getValueAt(...)
和setValueAt(...)
等使用反射来获取/设置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));
}
}
使用此基类,创建表非常容易:
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);
}
}
}
这一切的优点是什么?
不必担心列索引
getValueAt(...)
和setValueAt(...)
不同如果您认为这是对反射的滥用,那么您也将许多使用良好的库(例如Google GSON)也视为对反射的滥用。
现在,请注意派生类如何通过字符串而非事件触发事件时指示哪个字段已更改:
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
fireTableCellUpdated((R) this, NUM_QUOTES_RECEIVED);
}
如果我们只使用这些字段,那将是很好的。但是用getDeclaredField()这样做会很烂:
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) {
}
}
但是,有了我建议的功能,就很容易了:
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);
}
如果您认为此功能无法为有用的编程工具开辟无限可能,那么您就缺乏想象力了;)
最佳答案
在Java中引入关键字将很困难,但是您可以引入新的符号组合。例如,在Java 8中,您可以编写
MyClass::myMethod
要获取Method Reference,这是Java 7中无法做到的。
您可以对字段进行类似的操作,并且有一些建议来支持属性引用。
你甚至可以写
HashSet<String>::new
获取对构造函数ALA
new HashSet<String>()
的引用您可以使用MethodHandles.Lookup.unreflect(Method)将方法转换为MethodHandle,并使用MethodHandles.Lookup.unreflectConstructor(Constructor)将构造函数转换为MethodHandle。
一旦有了MethodHandle,就可以为其设置对象。