19

If I have an inner class e.g.

class Outer{
    class Inner{}
}

Is there any way to check if an arbitrary Object is an instance of any Inner, regardless of its outer object? instanceof gives false when the objects are not Inners from the same Outer. I know a workaround is just to make Inner a static class, but I'm wondering if what I'm asking is possible.

Example:

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
Navigateur
  • 1,852
  • 3
  • 23
  • 39
  • Can you provide an example where `instanceof Inner` returns false? – cambecc Jul 04 '13 at 10:56
  • @cambecc, `o instanceof Outer.Inner` gives false when `o` is an instance of an `Inner` of any `Outer` other than the one you're calling it from. – Navigateur Jul 04 '13 at 11:21
  • Not sure what you mean by "any `Outer` other than the one you're calling from". A quick code example would be really useful for answering your question. You don't mean `Outer.Inner` and `OtherOuter.Inner` should be the same `Inner`, do you? – cambecc Jul 04 '13 at 12:12
  • @cambecc, example included. – Navigateur Jul 05 '13 at 10:31
  • Thanks for the example. Just tried running it; result is `true`. Are you sure you see `false`? A result of `false` would violate JLS §15.20.2, which says: _"At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast to the ReferenceType without raising a ClassCastException."_ We know `outer2.inner` can be cast to `Inner` because that is its type. Try changing the argument of `isInner` to type `Inner` and you will see no explicit cast is required and no compile error occurs. Not sure why you see `false`. – cambecc Jul 05 '13 at 11:06
  • Here's another use case - I want to hold a long-lived instance of a class (e.g. a `Runnable`) provided by a user calling my API but I want to ensure they're not accidentally preventing a surrounding instance from being GC'ed by passing in an anonymous/inner class. Long-lived anonymous classes can be a subtle source of memory leaks, and this check can help prevent them. – dimo414 Jul 23 '16 at 20:45

6 Answers6

21

And what about?

public static boolean isInnerClass(Class<?> clazz) {
    return clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers());
}

The method isMemberClass() will test if the method is a member (and not an anonymous or local class) and the second condition will verify that your member class is not static.

By the way, the documentation explains the differences between local, anonymous and nested classes.

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes.

dimo414
  • 47,227
  • 18
  • 148
  • 244
LaurentG
  • 11,128
  • 9
  • 51
  • 66
  • 3
    Just to note, `isMemberClass()` returns false for anonymous and local classes (which makes sense, but might be surprising). Use [`isAnonymousClass()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isAnonymousClass--) and [`isLocalClass()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isLocalClass--) if you're interested in these class types. – dimo414 Nov 02 '16 at 22:13
4

o instanceof Outer.Inner gives false when o is an instance of an Inner of any Outer other than the one you're calling it from.

This doesn't happen for me - I get true for o instanceof Inner regardless of which particular enclosing instance of Outer the o belongs to:

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();
  }
}

Tested with both Java 6 and 7.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
2

you could always:

getClass().getName()

and do a String comparison.

EDIT : to account for inheritance (among inner classes? who would do that?!) you could always loop through getSuperclass() and check for them as well, and even go after implemented interfaces.

radai
  • 23,949
  • 10
  • 71
  • 115
  • @IanRoberts - why not? 2 Inner instances will have the same fully qualified class name, regardless of the parent object – radai Jul 04 '13 at 10:43
  • Yes, but if `Inner2 extends Inner` then objects whose class is `Inner2` _are_ instances of `Inner` even though their class name is different. – Ian Roberts Jul 04 '13 at 10:44
  • @IanRoberts - right you are. still fixable though :-) (edit incoming) – radai Jul 04 '13 at 10:46
  • 1
    The name of a class doesn't reveal whether its static or not. – dimo414 Jul 23 '16 at 20:47
2

Did you try using getEnclosingClass():

Returns the immediately enclosing class of the underlying class. If the underlying class is a top level class this method returns null.

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

Getting the correct enclosing class of the object , IMHO is not so easy . Read this.

Somewhat of a hack would be :

object.getClass().getName().contains("Outer$");
Community
  • 1
  • 1
AllTooSir
  • 48,828
  • 16
  • 130
  • 164
0

The java.lang.Class.getEnclosingClass() method returns the immediately enclosing class of the underlying class. If this class is a top level class this method returns null.

The following example shows the usage of java.lang.Class.getEnclosingClass() method:

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();
   }
}

Output

ClassDemo$1Outer inner in...class ClassDemo

ClassDemo$1Outer$1Inner inner in...class ClassDemo$1Outer

Zaheer Ahmed
  • 28,160
  • 11
  • 74
  • 110
0

I was googling for finding out better answers, to find out that there are none out there.

Here is what I have which works pretty well:

    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);
    }

Will return true for local inner classes. isMemberClass and others do not work for this purpose.

mjs
  • 21,431
  • 31
  • 118
  • 200