检查私有静态内部类的实例

8

这个问题的标题听起来像一个不错的思想实验,但是很遗憾并不是这样。让我们假设以下代码结构:

class Outer {
    private static class Inner implements SomeInterface {
        ...
    }
    public static SomeInterface returnInner() {
        return new Inner();
    }
}

有没有一种好的、干净的方法来执行以下检查:

SomeInterface a  = A.returnInner();
if (a instanceof Outer.Inner)
    throw new Error("Ooops, something bad happened");

你以为这不是一个真实的例子吗?查看Arrays类和方法asList(T ...),它返回自己的List<T>实现, 这恰好是一个private static class ArrayList<T>(这和通常的ArrayList没有关系 - 感谢Sun和Oracle,没有你们我该如何是好)。我需要检查传递给我的方法是否不是这个包装器类的实例,因为它没有实现add()方法(它使用AbstractList的实现 - 它抛出一个UnsupportedOperationException)。

这不是你可以合理检查的事情。无论如何,你都必须担心其他不支持add操作的列表,比如Collections.unmodifiableList或Guava的ImmutableList,或者其他什么东西。 - user2357112
真的没有好的方法来检查这个吗?即使通过一些反射魔法? - Adrian Jałoszewski
当然,你可以使用一些不好的反射技巧,但这并不是更好的方法。当有人传入一个不支持“add”操作的类型时,你仍然会得到“UnsupportedOperationError”的错误提示,而现在你还必须处理安全管理器阻止你或类名更改时的“ClassNotFoundException”。 - user2357112
“静态内部”这个词是自相矛盾的。这是一个静态嵌套类。 - undefined
3个回答

4

真的没有好的方法来检查这个吗?即使通过一些反射魔法?

有一种方式,但无论如何都不是一个好的方式:

// WARNINIG: This is very fragile, please try avoiding code like this.
if (theirList.getClass().getEnclosingClass() != null) {
    // theirList is an instance of an inner class
}

问题不在于难以实现,而是结果变得非常不可靠:
  • 这个代码会拒绝可变的 List<T> 实现 - 例如,如果您决定自己实现一个嵌套类来实现 List<T>,它将被拒绝
  • 这个代码会接受顶级只读实现 - 如果 Oracle 决定将实现移动到顶级,您的代码将停止工作。
出于这些原因,最好尝试添加到该类中,捕获异常,并采取与调用者传递只读列表时相同的替代方案。

3

这是一种可怕、肮脏、容易出错的做法,但你可以检查类的名称。内部类的名称是封闭类的名称后跟$和内部类的名称。在这种情况下:

SomeInterface instance = Outer.returnInner();
if (instance.getClass().getName().equals(Outer.class.getName() + "$Inner")) {
    throw new RuntimeException("Ooops, something bad happened");
}

0
到目前为止,其他答案都没有概括到一个真实的“instanceof”实现,只是检查类的相等性。这不能处理“returnInner()”返回“Inner”的子类的情况。
使用反射来递归地检查所有超类和接口,模拟“instanceof”并不太困难。
private static boolean isInstanceOf(Class<?> clazz, String type) {
    if (clazz.getName().equals(type)) {
        return true;
    }

    Class<?> superClazz = clazz.getSuperclass();
    if (superClazz != null && isInstanceOf(superClazz, type)) {
        return true;
    }

    for (Class<?> interfaze : clazz.getInterfaces()) {
        if (isInstanceOf(interfaze, type)) {
            return true;
        }
    }

    return false;
}

使用方法:

if (isInstanceOf(o.getClass(), "my.package.Outer$Inner")) // or
if (isInstanceOf(o.getClass(), Outer.class.getName() + "$Inner"))

同样的脆弱性警告适用于所有对非公共元素进行反射使用的情况:这些元素之所以不是非公共的,是有原因的。原因是当库更新或者您的程序在不同的JRE版本下运行时,这些元素可能会在没有通知的情况下发生变化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接