Vì vậy, chúng ta có một tình huống khi chúng ta cần lấy tĩnh đối tượng lớp hoặc một tên đầy đủ / đơn giản của lớp mà không sử dụng MyClass.class
cú pháp rõ ràng .
Nó có thể thực sự tiện dụng trong một số trường hợp, ví dụ như logger cho kotlin các hàm cấp trên (trong trường hợp này kotlin tạo ra một lớp Java tĩnh không thể truy cập được từ mã kotlin).
Chúng tôi có một vài biến thể khác nhau để có được thông tin này:
new Object(){}.getClass().getEnclosingClass();
lưu ý bởi Tom Hawtin - tackline
getClassContext()[0].getName();
từ SecurityManager
ghi chú của Christoffer
new Throwable().getStackTrace()[0].getClassName();
bằng cách đếm ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
từ Keksi
và cuối cùng là tuyệt vời
MethodHandles.lookup().lookupClass();
từ Rein
Tôi đã chuẩn bị một jmh điểm chuẩn cho tất cả các biến thể và kết quả là:
# 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
Kết luận
- Biến thể tốt nhất để sử dụng , khá sạch sẽ và nhanh chóng khủng khiếp.
Chỉ khả dụng kể từ Java 7 và Android API 26!
MethodHandles.lookup().lookupClass();
- Trong trường hợp bạn cần chức năng này cho Android hoặc Java 6, bạn có thể sử dụng biến thể tốt thứ hai. Nó cũng khá nhanh, nhưng tạo ra một lớp ẩn danh ở mỗi nơi sử dụng :(
new Object(){}.getClass().getEnclosingClass();
Nếu bạn cần nó ở nhiều nơi và không muốn mã byte của bạn phình to do hàng tấn các lớp ẩn danh - SecurityManager
là bạn của bạn (tùy chọn tốt thứ ba).
Nhưng bạn không thể gọi getClassContext()
- nó được bảo vệ trong SecurityManager
lớp. Bạn sẽ cần một số lớp trợ giúp như thế này:
// 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())
}
- Bạn có thể không bao giờ cần sử dụng hai biến thể cuối cùng dựa trên
getStackTrace()
ngoại lệ hoặc Thread.currentThread()
. Rất không hiệu quả và chỉ có thể trả về tên lớp là a String
, không phải là Class<*>
thể hiện.
PS
Nếu bạn muốn tạo một cá thể logger cho các tiện ích kotlin tĩnh (như tôi :), bạn có thể sử dụng trình trợ giúp này:
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())
Ví dụ sử dụng:
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();
}