为什么java没有像.class这样的.field和.method反射关键字?

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)

这一切有什么好处?

  • 您可以通过自己的POJO行类设置表的内容,而不必担心列索引
  • 使用您自己的POJO行类来设置值是类型安全的,与getValueAt(...)和不同setValueAt(...)
  • 您可以使用一些用于行格式的现有POJO类轻松创建表

如果你认为这是滥用反思,那么你会认为像谷歌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)

如果你认为这个功能不会为有用的编程工具打开一个可能的世界,你就会缺乏想象力;)

Pet*_*rey 8

在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`对象无关.类似地,`:: new`与`Constructor`对象无关. (2认同)

Dan*_*lan 1

原因如下:

  1. Method fooIntMethod = A.foo(int).method;语法上不正确
  2. Field xField = A.x.field;现在你不能在x名为“field”的类中拥有一个字段。如果人们的代码中已经包含了这个怎么办?这不会向后兼容。
  3. 同样的情况也适用于A.foo().method. 如果响应已经有一个名为 的字段怎么办method

  • 这不是反对意见,而是反对意见。他明确要求新的语法。 (2认同)