Fabio
Fabio

Reputation: 363

Check if a class exists in a specific package

I need to check if a class exists in a package. I know I can use Class.forName to check if a class exists, but how do I verify if it is inside a specific package?

Upvotes: 0

Views: 880

Answers (1)

rzwitserloot
rzwitserloot

Reputation: 102978

Do not use Class.forName for this.

Class.forName takes a fully qualified name. Fully qualified names include the package, but also the outer classes, and therefore, aren't going to work here:

package pkg;
class Outer {
    class Inner {}
}

results in the fully qualified name, the name you'd have to pass to CFN, for Inner is: Class.forName("pkg.Outer.Inner"); - and how do you tell Outer is an outer class and not part of the package name?

Java does not have hierarchical packages; there is no relationship between pkg and pkg.subpkg, so your question hopefully does not involve 'how do I check if the package part starts with a certain string', as you shouldn't be asking that question in the java ecosystem.

Thus, let's move away from Class.forName.

Note that the class needs to be available at runtime, or it won't work. "Fortunately", if the class is not available at runtime and you want to determine the package given e.g. a fully qualified class name, because of the above issue with outer and inner classes, that job is literally impossible, so if that's what your question boiled down to, you can stop reading: No can do. Let's assume it is available at runtime.

You need a Class<?> object.

Each class is represented by an object, of the java.lang.Class<?> type. You need to obtain such an object and then you can determine which package it is in.

Strategy 1: Class.forName

Class.forName("pkg.Outer.Inner") will get you the Class<?> object and from there you can ask it what its package is, and that would get you pkg, which you presumably want to know. So that's one way: Given a string representing the fully qualified name of a class, toss it through Class.forName, and then operate on the Class object you get out of this.

Strategy 2: Class literals.

Java has special syntax to obtain the Class<?> object given a type reference. So, if you know the type reference when you write your code, you can use this:

package pkg;

class Outer {
  class Inner{}
  private static Class<?> innerClassObj = Inner.class;
}

However, if you can write it that way, you already know from which package that class is coming from at write time, so that makes your question entirely moot. Why try to figure out at runtime what you already know?

Just in case this is what you wanted to know: Check your imports, and in any major IDE, hold CMD (CTRL on non-macs), and click on the name, it'll take you to where it is defined, and the package will be listed right there. Or just float over it, that works in most IDEs just as well.

Strategy 3: From an object instance.

All objects have a .getClass() method which obtains the Class<?> instance representing how the object was created.

Careful though!

List<String> list = new ArrayList<String>() {
    @Override public boolean add (String other) {
        log.info("Added: {}", other);
        return super.add(other);
    }
};

This is perfectly valid, somewhat common and completely innocent java code. However, it means that now invoking list.getClass() and then asking for the name of that class gives you something like com.foo.internal.WhateverClassThatCodeShowedUpIn$1, because that is technically a subclass, and thus list is an instance of that. If you wanted to check if the object is 'of a class that is from the java.util package', then just looking at list.getClass() would incorrectly tell you it is not.

The fix is to be aware of this and to always (in a while loop) go through all the superclasses. list.getClass().getSuperclass() would resolve to the exact same instance as java.util.ArrayList.class would, invoking getSuperclass on that will get you to java.util.AbstractList.class, and from there, java.lang.Object.class and then null. java.util.List.class never shows up here - that is not a class, that is an interface. If you want those too - well, .getInterfaces() exists.

So, if you want to know: Is this object compatible with some class that is in some specific package - there is your answer. Only way is to use while loops (and if you want to check interfaces, a queue or recursive method even).

Strategy 4: Have it be given to you.

You can always just have a method that takes in a Class<?> as a parameter. Various APIs out there give you one, as well.

Okay, I have a Class<?> instance, now what?

You could call the .getPackage() method on it, but unfortunately the JVM spec dictates that this doesn't actually have to return something (it may return null). So that's not a great solution. Instead, I suggest you invoke .getName() on it, and then go to town on the string you get.

That string you get would be pkg.Outer$Inner. You can see how you can derive the package from this:

  1. Find the last ..
  2. If it exists, strip that and all after it.
  3. If there is no dot at all, it's in the unnamed package.

Voila. That'll leave you with pkg.

NB: Take into account the bit written about in strategy 3: For your needs you may have to scan through the superclass and all superinterfaces, recursively.

Upvotes: 1

Related Questions