Какой бы ни была причина, в моем абстрактном классе реализовано следующее hashCode.
@MappedSuperclass
abstract Some {
@Override
public boolean equals(final Object obj) {
// ...
}
@Override
public int hashCode() {
return getClass().hashCode(); // TODO: cache, maybe?
}
}
getClass().hashCode()
?Могу ли я кэшировать значение getClass().hashCode()?
Можно, но...
Есть ли возможность изменения значения во время работы в JVM?
Нет возможности. Класс объекта Java не может измениться, и хэш-код объекта Java Class (который является типом результата для getClass()) не может измениться.
Это преждевременная оптимизация?
Наверное да1.
Однако использование хэш-кода класса объекта в качестве хэш-кода объекта — очень плохая идея с точки зрения производительности.
Это означает, что все экземпляры класса (например, Some) будут иметь один и тот же хэш-код 2. Это приведет к миллиону коллизий хешей и сделает большинство операций HashSet и HashMap O(N) или O(logN) (в зависимости от вашей версии Java), а не O(1).
1 - I am assuming you have not done a bunch of performance analysis that you haven't told us about. If you had already done the analysis, then maybe this is not a premature optimization. 2 - I am assuming that the Some::hashCode method is not overridden to something more sensible by concrete subclasses of Some.
Это хороший ответ. У меня есть одна путаница: может ли быть несколько экземпляров Class<Some>, если несколько загрузчиков классов загружают класс Some. Если да, то в этом случае getClass().hashcode() может возвращать разные значения.
Я говорил о нескольких загрузчиках классов, загружающих класс Some. Потому что getClass().hashcode() учится в Some классе. Итак, если класс Some загружается несколькими загрузчиками классов, будет ли getClass().hashcode() возвращать разные значения для них обоих?
А... я неправильно понял. Если несколько загрузчиков классов загружают Some, то это будут разные классы (с точки зрения системы типов Java), и они будут иметь разные объекты Class<Some> и их хэш-коды, вероятно, будут разными.
Ответ Стивена С - хороший ответ. Однако для полноты я считаю необходимым добавить к этому, что если класс загружается, скажем, двумя разными загрузчиками классов, то для этого класса getClass().hashcode() вернет два разных значения.
Чтобы убедиться в этом, я написал программу и загрузил класс с помощью системного загрузчика классов, а затем с помощью своего собственного пользовательского загрузчика классов. Оба возвращают разные hashcode():
Сначала код для пользовательского загрузчика классов, который я скопировал с https://www.digitalocean.com/community/tutorials/java-classloader:
package com.journaldev.classloader;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Our Custom ClassLoader to load the classes. Any class in the
* com.journaldev package will be loaded using this ClassLoader.
* For other classes, it will delegate the request to its Parent
* ClassLoader.
*/
public class CCLoader extends ClassLoader {
/**
* This constructor is used to set the parent ClassLoader
*/
public CCLoader(ClassLoader parent) {
super(parent);
}
/**
* Loads the class from the file system. The class file should be located in
* the file system. The name should be relative to get the file location
*
* @param name Fully Classified name of the class, for example, com.journaldev.Foo
*/
private Class getClass(String name) throws ClassNotFoundException {
String file = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
// This loads the byte code data from the file
b = loadClassFileData(file);
// defineClass is inherited from the ClassLoader class
// that converts byte array into a Class. defineClass is Final
// so we cannot override it
Class c = defineClass(name, b, 0, b.length);
resolveClass(c);
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Every request for a class passes through this method. If the class is in
* com.journaldev package, we will use this classloader or else delegate the
* request to parent classloader.
*
* @param name Full class name
*/
@Override
public Class loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("com.journaldev.")) {
return getClass(name);
}
return super.loadClass(name);
}
/**
* Reads the file (.class) into a byte array. The file should be
* accessible as a resource and make sure that it's not in Classpath to avoid
* any confusion.
*
* @param name Filename
* @return Byte array read from the file
* @throws IOException if an exception comes in reading the file
*/
private byte[] loadClassFileData(String name) throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
name);
int size = stream.available();
byte buff[] = new byte[size];
DataInputStream in = new DataInputStream(stream);
in.readFully(buff);
in.close();
return buff;
}
}
Затем класс Test и основной метод, который мы будем использовать для загрузки класса Test с помощью разных загрузчиков классов:
package com.journaldev.test;
import com.journaldev.classloader.CCLoader;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(new CCLoader(ClassLoader.getSystemClassLoader())
.loadClass(Test.class.getCanonicalName()).hashCode());
System.out.println(ClassLoader.getSystemClassLoader()
.loadClass(Test.class.getCanonicalName()).hashCode());
}
}
Вывод показывает разные хэш-коды для одного и того же класса, загруженного разными загрузчиками классов:
1554547125
1072591677
Не зная, для чего вам это нужно, это звучит как преждевременная оптимизация.