And*_*ndy 5 java reflection methods field keyword
假设我们有这个课程:
public abstract class A {
int x;
public int foo();
public int foo(int x);
}
Run Code Online (Sandbox Code Playgroud)
众所周知,A.class它具有明显的优势Class.forName("A"):如果A通过重构或混淆改变名称,它仍然可以工作.
但是,没有办法通过字段和方法获得这种优势.你有没有希望你能做到这一点:( 在下面的编辑中看到一个更好的建议语法!)
Field xField = A.x.field;
Method fooMethod = A.foo().method;
Method fooIntMethod = A.foo(int).method;
Run Code Online (Sandbox Code Playgroud)
而不是这个?
Field xField = A.getField("x");
Method fooMethod = A.getMethod("foo");
Method fooIntMethod = A.getMethod("foo", int.class);
Run Code Online (Sandbox Code Playgroud)
所以这是我的问题:有没有人知道这个功能是否已经计划或讨论过,或者Sun/Oracle是否出于某种原因特别决定反对它?
编辑:这个语法怎么样?它避免了人们提到的问题:
Field xField = A..x;
Method fooMethod = A..foo();
Method fooIntMethod = A..foo(int);
Run Code Online (Sandbox Code Playgroud)
用例示例
我最近创建了一个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));
}
}
Run Code Online (Sandbox Code Playgroud)
使用这个基类,创建表格非常容易:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这一切有什么好处?
getValueAt(...)和不同setValueAt(...)如果你认为这是滥用反思,那么你会认为像谷歌GSON这样许多使用良好的库也会滥用反思.
现在,请注意派生类如何通过String而不是字段触发事件时指示哪个字段已更改:
public void setNumQuotesReceived(Integer numQuotesReceived) {
this.numQuotesReceived = numQuotesReceived;
fireTableCellUpdated((R) this, NUM_QUOTES_RECEIVED);
}
Run Code Online (Sandbox Code Playgroud)
如果我们可以使用这些字段会很好.但是使用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) {
}
}
Run Code Online (Sandbox Code Playgroud)
但是,根据我提议的功能,它很容易作为蛋糕:
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);
}
Run Code Online (Sandbox Code Playgroud)
如果你认为这个功能不会为有用的编程工具打开一个可能的世界,你就会缺乏想象力;)
在Java中引入关键字很困难,但是您可以引入新的符号组合.例如,在Java 8中,您可以编写
MyClass::myMethod
Run Code Online (Sandbox Code Playgroud)
获取方法参考,这是Java 7中无法做到的.
你可以用字段做类似的事情,并且有建议支持属性引用.
你甚至可以写
HashSet<String>::new
Run Code Online (Sandbox Code Playgroud)
获取对构造函数ALA的引用 new HashSet<String>()
您可以使用MethodHandles.Lookup.unreflect(Method)将Method转换为MethodHandle, 您可以使用MethodHandles.Lookup.unreflectConstructor(Constructor)将Constructor转换为MethodHandle
一旦有了MethodHandle,就可以设置一个对象来调用它.
原因如下:
Method fooIntMethod = A.foo(int).method;语法上不正确Field xField = A.x.field;现在你不能在x名为“field”的类中拥有一个字段。如果人们的代码中已经包含了这个怎么办?这不会向后兼容。A.foo().method. 如果响应已经有一个名为 的字段怎么办method?