Jadi, kita memiliki situasi ketika kita perlu secara statis mendapatkan objek kelas atau nama lengkap / kelas sederhana tanpa penggunaan MyClass.class
sintaks yang eksplisit .
Ini bisa sangat berguna dalam beberapa kasus, misalnya contoh logger untuk kotlin fungsi tingkat atas (dalam hal ini kotlin membuat kelas Java statis yang tidak dapat diakses dari kode kotlin).
Kami memiliki beberapa varian berbeda untuk mendapatkan informasi ini:
new Object(){}.getClass().getEnclosingClass();
dicatat oleh Tom Hawtin - tackline
getClassContext()[0].getName();
dari yang SecurityManager
dicatat oleh Christoffer
new Throwable().getStackTrace()[0].getClassName();
menurut hitungan ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
dari Keksi
dan akhirnya dahsyat
MethodHandles.lookup().lookupClass();
dari Rein
Saya sudah menyiapkan jmh tolok ukur untuk semua varian dan hasil adalah:
# Run complete. Total time: 00:04:18
Benchmark Mode Cnt Score Error Units
StaticClassLookup.MethodHandles_lookup_lookupClass avgt 30 3.630 ± 0.024 ns/op
StaticClassLookup.AnonymousObject_getClass_enclosingClass avgt 30 282.486 ± 1.980 ns/op
StaticClassLookup.SecurityManager_classContext_1 avgt 30 680.385 ± 21.665 ns/op
StaticClassLookup.Thread_currentThread_stackTrace_1_className avgt 30 11179.460 ± 286.293 ns/op
StaticClassLookup.Throwable_stackTrace_0_className avgt 30 10221.209 ± 176.847 ns/op
Kesimpulan
- Varian terbaik untuk digunakan , agak bersih dan sangat cepat.
Hanya tersedia sejak Java 7 dan Android API 26!
MethodHandles.lookup().lookupClass();
- Jika Anda membutuhkan fungsi ini untuk Android atau Java 6, Anda dapat menggunakan varian terbaik kedua. Ini agak cepat juga, tetapi menciptakan kelas anonim di setiap tempat penggunaan :(
new Object(){}.getClass().getEnclosingClass();
Jika Anda membutuhkannya di banyak tempat dan tidak ingin kode byte Anda kembung karena banyak kelas anonim - SecurityManager
adalah teman Anda (opsi terbaik ketiga).
Tapi Anda tidak bisa begitu saja menelepon getClassContext()
- itu dilindungi di SecurityManager
kelas. Anda membutuhkan beberapa kelas pembantu seperti ini:
// Helper class
public final class CallerClassGetter extends SecurityManager
{
private static final CallerClassGetter INSTANCE = new CallerClassGetter();
private CallerClassGetter() {}
public static Class<?> getCallerClass() {
return INSTANCE.getClassContext()[1];
}
}
// Usage example:
class FooBar
{
static final Logger LOGGER = LoggerFactory.getLogger(CallerClassGetter.getCallerClass())
}
- Anda mungkin tidak perlu menggunakan dua varian terakhir berdasarkan
getStackTrace()
dari atau dari Thread.currentThread()
. Sangat tidak efisien dan hanya dapat mengembalikan nama kelas sebagai String
, bukan Class<*>
turunannya.
PS
Jika Anda ingin membuat instance logger untuk utilitas kotlin statis (seperti saya :), Anda dapat menggunakan bantuan ini:
import org.slf4j.Logger
import org.slf4j.LoggerFactory
// Should be inlined to get an actual class instead of the one where this helper declared
// Will work only since Java 7 and Android API 26!
@Suppress("NOTHING_TO_INLINE")
inline fun loggerFactoryStatic(): Logger
= LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())
Contoh penggunaan:
private val LOGGER = loggerFactoryStatic()
/**
* Returns a pseudo-random, uniformly distributed value between the
* given least value (inclusive) and bound (exclusive).
*
* @param min the least value returned
* @param max the upper bound (exclusive)
*
* @return the next value
* @throws IllegalArgumentException if least greater than or equal to bound
* @see java.util.concurrent.ThreadLocalRandom.nextDouble(double, double)
*/
fun Random.nextDouble(min: Double = .0, max: Double = 1.0): Double {
if (min >= max) {
if (min == max) return max
LOGGER.warn("nextDouble: min $min > max $max")
return min
}
return nextDouble() * (max - min) + min
}
try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();
}