How to call getClass() from a static method in Java?
Asked Answered
K

9

424

I have a class that must have some static methods. Inside these static methods I need to call the method getClass() to make the following call:

public static void startMusic() {
  URL songPath = getClass().getClassLoader().getResource("background.midi");
}

However Eclipse tells me:

Cannot make a static reference to the non-static method getClass() 
from the type Object

What is the appropriate way to fix this compile time error?

Killigrew answered 26/11, 2011 at 0:50 Comment(1)
Using getResource() before there is an instance of a user defined (e.g. non-J2SE) class will sometimes fail. The problem is that the JRE will be using the bootstrap class-loader at that stage, which will not have application resources on the class-path (of the bootstrap loader).Triplett
T
719

The Answer

Just use TheClassName.class instead of getClass().

Declaring Loggers

Since this gets so much attention for a specific usecase--to provide an easy way to insert log declarations--I thought I'd add my thoughts on that. Log frameworks often expect the log to be constrained to a certain context, say a fully-qualified class name. So they are not copy-pastable without modification. Suggestions for paste-safe log declarations are provided in other answers, but they have downsides such as inflating bytecode or adding runtime introspection. I don't recommend these. Copy-paste is an editor concern, so an editor solution is most appropriate.

In IntelliJ, I recommend adding a Live Template:

  • Use "log" as the abbreviation
  • Use private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class); as the template text.
  • Click Edit Variables and add CLASS using the expression className()
  • Check the boxes to reformat and shorten FQ names.
  • Change the context to Java: declaration.

Now if you type log<tab> it'll automatically expand to

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

And automatically reformat and optimize the imports for you.

Tackle answered 26/11, 2011 at 0:51 Comment(6)
This will result in a copy/paste and sad sad results when the incorrect code is running in a different class.Mandell
@WilliamEntriken: Using anonymous inner classes isn't a great solution to that, it just bloats your bytecode with extra classfiles to save a couple seconds of developer effort. I'm not sure what people are doing where they have to copy/paste all over the place, but it should be treated with an editor solution, not a language hack. e.g. if using IntelliJ, use a Live Template which can insert the classname.Tackle
“I'm not sure what people are doing where they have to copy/paste all over the place”—something such as using Log in Android, which requires a tag to be supplied, typically the class name. And there are probably other examples. Of course you can declare a field for that (which is common practice), but that is still copy and paste.Jeffery
@user149408: Yes this has been talked about a lot. Though this usecase had really nothing to do with the original question, it's a common reason people visit this question so I've added some thoughts to the answer.Tackle
You forgot one thing in your Live Template solution: Click "Edit variables" and in the Expression for CLASS type in className(). That will make sure that $CLASS$ is actually replaced with the class name, otherwise it will just come out empty.Applesauce
ClassName.class doesn't solve the static reference issue if the class is abstract. When extending the abstract, ClassName.class will refer to the abstract class rather than the subclass, whereas this.getClass() in a non-static method will refer to the subclass.Kavita
S
135

As for the code example in the question, the standard solution is to reference the class explicitly by its name, and it is even possible to do without getClassLoader() call:

class MyClass {
  public static void startMusic() {
    URL songPath = MyClass.class.getResource("background.midi");
  }
}

This approach still has a back side that it is not very safe against copy/paste errors in case you need to replicate this code to a number of similar classes.

And as for the exact question in the headline, there is a trick posted in the adjacent thread:

Class currentClass = new Object() { }.getClass().getEnclosingClass();

It uses a nested anonymous Object subclass to get hold of the execution context. This trick has a benefit of being copy/paste safe...

Caution when using this in a Base Class that other classes inherit from:

It is also worth noting that if this snippet is shaped as a static method of some base class then currentClass value will always be a reference to that base class rather than to any subclass that may be using that method.

Shaia answered 1/7, 2013 at 4:39 Comment(2)
By far my favorite answer. NameOfClass.class is obvious, but that requires you to know your class's name. The getEnclosingClass trick is not just useful for copy-pasting, also useful for static base class methods that make use of introspectionRochellerochemont
Incidentally Class.getResource() may behave slightly differently from ClassLoader.getResource(); see stackoverflow.com/a/676273Cloddish
S
89

In Java7+ you can do this in static methods/fields:

MethodHandles.lookup().lookupClass()
Saccular answered 6/4, 2015 at 18:18 Comment(3)
Nice solution if you only need getSimpleName() call to print help from main method.Lauritz
I was looking for a 'copy paste' solution for adding logger every where, without changing the class name each time. You gave it to me : private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());Begot
The best solution where it’s supported, drawback being that Android does not support this until API 26, i.e. Android 8.Jeffery
B
27

I wrestled with this myself. A nice trick is to use use the current thread to get a ClassLoader when in a static context. This will work in a Hadoop MapReduce as well. Other methods work when running locally, but return a null InputStream when used in a MapReduce.

public static InputStream getResource(String resource) throws Exception {
   ClassLoader cl = Thread.currentThread().getContextClassLoader();
   InputStream is = cl.getResourceAsStream(resource);
   return is;
}
Barbette answered 14/3, 2014 at 18:45 Comment(0)
S
16

Simply use a class literal, i.e. NameOfClass.class

Sambo answered 26/11, 2011 at 0:52 Comment(0)
F
13

Try it

Thread.currentThread().getStackTrace()[1].getClassName()

Or

Thread.currentThread().getStackTrace()[2].getClassName()
Filippo answered 21/1, 2016 at 7:53 Comment(1)
The beauty of this approach is that it will also work on Android versions prior to 8 (API 26). Beware that index [1] will cause all log messages to report Thread as their source on Android 4.4.4. [2] seems to give the correct result on both Android and OpenJRE.Jeffery
C
11

getClass() method is defined in Object class with the following signature:

public final Class getClass()

Since it is not defined as static, you can not call it within a static code block. See these answers for more information: Q1, Q2, Q3.

If you're in a static context, then you have to use the class literal expression to get the Class, so you basically have to do like:

Foo.class

This type of expression is called Class Literals and they are explained in Java Language Specification Book as follows:

A class literal is an expression consisting of the name of a class, interface, array, or primitive type followed by a `.' and the token class. The type of a class literal is Class. It evaluates to the Class object for the named type (or for void) as defined by the defining class loader of the class of the current instance.

You can also find information about this subject on API documentation for Class.

Composer answered 26/11, 2011 at 2:1 Comment(0)
A
3

I had the same problem ! but to solve it just modify your code as following.

public static void startMusic() {
URL songPath = YouClassName.class.getClassLoader().getResource("background.midi");
}

this worked fine with me hope it will also work fine with you.

Andante answered 1/9, 2020 at 16:23 Comment(1)
what is "YourClassName"? should be 'startMusic"? Anyway to avoid specifying class name, can't we use just 'this.class.getClassLoader()'?Escamilla
N
0

Suppose there is a Utility class, then sample code would be -

    URL url = Utility.class.getClassLoader().getResource("customLocation/".concat("abc.txt"));

CustomLocation - if any folder structure within resources otherwise remove this string literal.

Nectar answered 9/3, 2020 at 8:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.