Java:如何检查一个对象是否是非静态内部类的实例,而不管外部对象是什么?

19
如果我有一个内部类,例如:
class Outer{
    class Inner{}
}

有没有办法检查任意Object是否是任何Inner的实例,而不考虑其外部对象?instanceof在对象不是来自同一OuterInner时会返回false。我知道一个解决方法就是将Inner定义为静态类,但我想知道我所问的是否可能。

示例:

class Outer{
    Inner inner = new Inner();
    class Inner{}

    public boolean isInner(Object o){
        return o instanceof Inner;
    }
}


Outer outer1 = new Outer();
Outer outer2 = new Outer();
boolean answer = outer1.isInner(outer2.inner); //gives false

你能提供一个instanceof Inner返回false的例子吗? - cambecc
@cambecc,当o是除了你正在调用的那个Outer之外的任何OuterInner的实例时,o instanceof Outer.Inner会返回false。 - Navigateur
不确定你所说的“除了当前调用的那个‘Outer’之外还有哪个‘Outer’”,一个快速的代码示例将非常有用来回答你的问题。你不是指Outer.InnerOtherOuter.Inner应该是相同的Inner,是吗? - cambecc
@cambecc, 包括示例。 - Navigateur
感谢提供示例代码。我刚试运行了它,结果是“true”。您确定您看到的是“false”吗?如果结果为“false”,那将会违反JLS §15.20.2的规定,该规定指出:“在运行时,instanceof 运算符的结果为 true 当且仅当 RelationalExpression 的值不为 null 且引用可以转换为 ReferenceType,而不引发 ClassCastException。”我们知道 outer2.inner 可以被转换为 Inner,因为这是它的类型。尝试更改 isInner 的参数类型为 Inner,您会发现无需进行显式转换并且不会出现编译错误。我不确定您为什么看到 false - cambecc
以下是另一个使用案例 - 我想要持有一个由用户调用我的API提供的类(例如Runnable)的长期实例,但我希望确保他们不会通过传递匿名/内部类来意外地阻止周围实例被GC回收。长期存在的匿名类可能是内存泄漏的微妙来源,而这个检查可以帮助防止它们。 - dimo414
6个回答

21

那又怎样?

public static boolean isInnerClass(Class<?> clazz) {
    return clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers());
}
方法isMemberClass()将会检测方法是否为成员类(而不是匿名类或局部类),第二个条件将验证您的成员类是否不是静态的。
顺便说一下,文档解释了本地、匿名和嵌套类之间的区别。

嵌套类分为两类:静态和非静态。声明为静态的嵌套类被称为静态嵌套类。非静态嵌套类称为内部类。


3
请注意,isMemberClass() 对于匿名类和本地类返回 false(这很合理,但可能会令人惊讶)。如果您对这些类类型感兴趣,请使用 isAnonymousClass()isLocalClass() - dimo414

4

o instanceof Outer.Innero是任何一个不同于你调用的那个Outer的任何一个Inner的实例时,返回false

但是这对我来说并不适用 - 对于o instanceof Inner无论o属于哪个具体的包含Outer的实例,我都会得到true

class Outer {
  class Inner {}

  void test() {
    // Inner instance that belongs to this Outer
    Inner thisInner = new Inner();

    // Inner instance that belongs to a different Outer
    Outer other = new Outer();
    Inner otherInner = other.new Inner();

    // both print true
    System.out.println(thisInner instanceof Inner);
    System.out.println(otherInner instanceof Inner);
  }

  public static void main(String[] args) {
    new Outer().test();
  }
}

已经测试过Java 6和7两个版本。


2

你可以始终:

getClass().getName()

并进行字符串比较。

编辑:考虑到继承(在内部类中?谁会这么做!)你可以通过循环getSuperclass()并检查它们,甚至可以追溯实现的接口。


@IanRoberts - 为什么不呢?两个内部实例的完全限定类名将相同,无论父对象如何。 - radai
是的,但是如果Inner2 extends Inner,那么其类为Inner2的对象即使类名不同,它们也是Inner的实例。 - Ian Roberts
@IanRoberts - 你说得对。不过还是可以修复的 :-)(即将编辑) - radai
1
一个类的名称并不能表明它是否为静态。 - dimo414

2

你尝试使用getEnclosingClass()了吗:

返回底层类的直接封闭类。如果底层类是顶级类,则此方法返回null。

Outer.class.equals(object.getClass().getEnclosingClass())

在我看来,正确获取对象的封闭类并不容易。请阅读此处

一种有些hack的方法是:

object.getClass().getName().contains("Outer$");

但这也适用于任何假设的OtherInner吗? - radai

0

java.lang.Class.getEnclosingClass() 方法返回底层类的直接封闭类。如果此类是顶级类,则此方法返回 null。

以下示例显示了 java.lang.Class.getEnclosingClass() 方法的用法:

import java.lang.*;

public class ClassDemo {
   // constructor
   public ClassDemo() {

      // class Outer as inner class for class ClassDemo
      class Outer {

         public void show() {
            // inner class of Class Outer
            class Inner {

               public void show() {
                  System.out.print(getClass().getName() + " inner in...");
                  System.out.println(getClass().getEnclosingClass());    
               }
            }
            System.out.print(getClass().getName() + " inner in...");
            System.out.println(getClass().getEnclosingClass());

            // inner class show() function
            Inner i = new Inner();
            i.show();
         }
      }

      // outer class show() function
      Outer o = new Outer();
      o.show();
   }

   public static void main(String[] args) {
     ClassDemo cls = new ClassDemo();
   }
}

输出

ClassDemo$1Outer内部类在...类ClassDemo中

ClassDemo$1Outer$1Inner内部类在...类ClassDemo$1Outer中


0

我在谷歌上搜索以找到更好的答案,结果发现并没有。

这是我目前拥有的相当不错的解决方案:

    public static boolean isStatic(Class klass) {
            return Modifier.isStatic(klass.getModifiers());
    }

    /**
     * Non static inner class
     */
    public static boolean isInnerclass(Class klass) {
            return klass.getDeclaringClass() != null && !isStatic(klass);
    }

对于本地内部类将返回true。isMemberClass和其他方法不能用于此目的。


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