英文:
How to get class which invokes static method?
问题
以下是您的代码的翻译部分:
这是我的代码:
class A {
public static void doIt() {
//Class currentClass = new Object() { }.getClass().getEnclosingClass();
//Class currentClass = MethodHandles.lookup().lookupClass();
String currentClass = Thread.currentThread().getStackTrace()[1].getClassName();
System.out.println("调用类:" + currentClass);
}
}
class B extends A { }
public class NewMain {
public static void main(String[] args) {
A.doIt();
B.doIt();
}
}
如您所见,doIt
方法可以由 A
和 B
类调用。在 doIt
方法中,我想知道是哪个类用于调用该方法(A
还是 B
)。这是可能的吗?我尝试过的三种解决方案都没有起作用 - 它总是显示 A
类。
英文:
This is my code:
class A {
public static void doIt() {
//Class currentClass = new Object() { }.getClass().getEnclosingClass();
//Class currentClass = MethodHandles.lookup().lookupClass();
String currentClass = Thread.currentThread().getStackTrace()[1].getClassName();
System.out.println("CALLING CLASS:" + currentClass);
}
}
class B extends A { }
public class NewMain {
public static void main(String[] args) {
A.doIt();
B.doIt();
}
}
As you see doIt
method can be called by A
and B
classes. In doIt
I want to know what class was used to call method (A
or B
). Is it possible? Three solutions I tried didn't work - it always says A
class.
答案1
得分: 2
起初,我认为这是不可能的,因为 Java 编译器可以找出将调用哪个方法并生成相同的指令。
事实证明它实际上记录了调用类的方式。
所以,现在的问题是:
* 我们如何获得调用方法的位置?
* 我们如何使用这些信息来获取方法被调用的方式?
第一个问题很容易解决:我们使用 StackWalker,它可以给我们提供[字节码索引](https://download.java.net/java/GA/jdk14/docs/api/java.base/java/lang/StackWalker.StackFrame.html#getByteCodeIndex%28%29)。
现在我们只需要解析类,查看该字节码索引处的指令,然后找出如何调用此方法。
我使用了 ASM 来实现,但在这里可能不是最合适的工具。
```java
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.StackWalker.StackFrame;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
class CallingClassVisitor extends ClassVisitor {
private final StackFrame where;
String ownerClass = null;
public CallingClassVisitor(StackFrame where) {
// 我们需要一个 ClassWriter 作为支持,以便解析 Label。
super(ASM8, new ClassWriter(0));
this.where = where;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor parent = super.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals(where.getMethodName()) && descriptor.equals(where.getDescriptor())) {
return new CallingMethodVisitor(where, parent);
} else {
return parent;
}
}
class CallingMethodVisitor extends MethodVisitor {
private final StackFrame where;
public CallingMethodVisitor(StackFrame where, MethodVisitor parent) {
super(ASM8, parent);
this.where = where;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
Label lbl = new Label();
visitLabel(lbl);
if (lbl.getOffset() == where.getByteCodeIndex()) {
ownerClass = owner;
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
public String getOwnerClass() {
return ownerClass;
}
}
class A {
static final StackWalker SW = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE));
public static void doIt() {
StackFrame sf = SW.walk(s -> s.skip(1).findFirst()).orElseThrow();
InputStream source = sf.getDeclaringClass().getClassLoader()
.getResourceAsStream(sf.getClassName().replace('.', '/') + ".class");
try {
CallingClassVisitor ccv = new CallingClassVisitor(sf);
new ClassReader(source).accept(ccv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
String how = ccv.getOwnerClass();
System.out.println(how);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
class B extends A { }
public class NewMain {
public static void main(String[] args) {
A.doIt();
B.doIt();
}
}
最终,我不确定您的要求是否值得这样的努力。
<details>
<summary>英文:</summary>
At first, I thought this is impossible as the java compiler can figure out which method will be called and emit the same instruction.
Turns out that it actually records the way the class is called.
So, the question now becomes:
* How do we get the place where the method is called?
* How do we use this information to get the way the method is called?
The first one is easy: We use a StackWalker, which can give us the [bytecode index](https://download.java.net/java/GA/jdk14/docs/api/java.base/java/lang/StackWalker.StackFrame.html#getByteCodeIndex%28%29).
Now we only need to parse the class, look at the instruction at that bytecode index, and figure out how this method was called.
I used ASM for that, but it might be the wrong tool here.
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.StackWalker.StackFrame;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
class CallingClassVisitor extends ClassVisitor {
private final StackFrame where;
String ownerClass = null;
public CallingClassVisitor(StackFrame where) {
// We need a backing ClassWriter, so the Label is resolved.
super(ASM8, new ClassWriter(0));
this.where = where;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor parent = super.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals(where.getMethodName()) && descriptor.equals(where.getDescriptor())) {
return new CallingMethodVisitor(where, parent);
} else {
return parent;
}
}
class CallingMethodVisitor extends MethodVisitor {
private final StackFrame where;
public CallingMethodVisitor(StackFrame where, MethodVisitor parent) {
super(ASM8, parent);
this.where = where;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
Label lbl = new Label();
visitLabel(lbl);
if (lbl.getOffset() == where.getByteCodeIndex()) {
ownerClass = owner;
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
public String getOwnerClass() {
return ownerClass;
}
}
class A {
static final StackWalker SW = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE));
public static void doIt() {
StackFrame sf = SW.walk(s -> s.skip(1).findFirst()).orElseThrow();
InputStream source = sf.getDeclaringClass().getClassLoader()
.getResourceAsStream(sf.getClassName().replace('.', '/') + ".class");
try {
CallingClassVisitor ccv = new CallingClassVisitor(sf);
new ClassReader(source).accept(ccv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
String how = ccv.getOwnerClass();
System.out.println(how);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
class B extends A { }
public class NewMain {
public static void main(String[] args) {
A.doIt();
B.doIt();
}
}
In the end, I'm not sure if your requirement is worth that effort.
</details>
# 答案2
**得分**: 0
以下是翻译好的部分:
```java
如何看待这个?我知道它并不完全符合您的要求,但正如其他人已经指出的那样...... 您可能需要重新考虑设计方法。
class A {
protected static Class childClass; // 或者选择 private
// protected static Class getChildClass { return childClass; }
public static void setChildClass(Class classReference) {
childClass = classReference;
}
public static void doIt() {
System.out.println("CLASS:" + childClass.getSimpleName());
}
}
class B extends A {
// doIt() 将被继承
// 带参数的方法重载
public static void doIt(String message) {
System.out.println("CLASS:" + B.class.getSimpleName() + ", message: " + message);
}
}
public class NewMain {
public static void main(String[] args) {
A.setChildClass(B.class);
A.doIt(); // CLASS:B
B.doIt(); // CLASS:B
B.doIt("Hello") // CLASS:B, message: Hello
}
}
**编辑**
您可以拥有一个简单的控制器和一些委托来实现类似的功能。虽然不全是静态的,但您对各个部分有更多的控制。请注意,这个示例故意非常基本。
您还可以拥有类 A 和类 B,其中 B 将 A 作为依赖,并且您只需重写 A(委托)应有的不同操作。在这里,也不是静态的,但我认为这并不是您的主要问题。毕竟问题更多地与设计有关。
import java.util.*;
public class MyClass {
public static void main(String... args) {
final DoItController doItController = DoItController.Singleton.getInstance();
doItController.doIt(DoerDelegateA.class);
doItController.doIt(DoerDelegateB.class);
doItController.doIt(DoerDelegateComplex.class);
}
}
interface Doer {
void doIt();
}
class DoItController {
final Map<Class<? extends DoerDelegate>, DoerDelegate> doerDelegateMap;
private DoItController() {
doerDelegateMap = new LinkedHashMap();
doerDelegateMap.put(DoerDelegateA.class, new DoerDelegateA());
doerDelegateMap.put(DoerDelegateB.class, new DoerDelegateB());
doerDelegateMap.put(DoerDelegateComplex.class, new DoerDelegateComplex());
}
public void doIt(Class<? extends DoerDelegate> delegateReference) {
final DoerDelegate delegate = doerDelegateMap.getOrDefault(delegateReference, null);
if (delegate != null) {
delegate.doIt();
}
}
public static class Singleton {
private static DoItController instance;
public static DoItController getInstance() {
if (instance == null) {
instance = new DoItController();
}
return instance;
}
}
}
class DoerDelegate implements Doer {
@Override
public void doIt() {
System.out.println("使用:" + getClass().getSimpleName());
}
}
class DoerDelegateA extends DoerDelegate {}
class DoerDelegateB extends DoerDelegate {}
class DoerDelegateComplex extends DoerDelegateA {
@Override
public void doIt() {
System.out.println("使用重写的标准方法:" + getClass().getSimpleName());
}
}
或者...
public class MyClass {
public static void main(String... args) {
final B b = new B();
b.doIt();
}
}
interface Doer {
void doIt();
}
class A implements Doer {
@Override
public void doIt() { System.out.println("执行它"); }
}
class B implements Doer {
final A aDelegate;
public B() {
aDelegate = new A();
}
@Override
public void doIt() {
// 标准操作
aDelegate.doIt();
// 您特定的 B 操作
//...
}
}
如果还有其他需要翻译的部分,请告诉我。
英文:
How about this one here? I know its not exactly what you want but as others already stated ... you might want to rethink your design approach.
class A {
protected static Class childClass; // alt. private
// protected static Class getChildClass { return childClass; }
public static void setChildClass(Class classReference) {
childClass = classReference;
}
public static void doIt() {
System.out.println("CLASS:" + childClass.getSimpleName());
}
}
class B extends A {
// doIt() will be inherited
// overloading method with a param
public static void doIt(String message) {
System.out.println("CLASS:" + B.class.getSimpleName() + ", message: " + message);
}
}
public class NewMain {
public static void main(String[] args) {
A.setChildClass(B.class);
A.doIt(); // CLASS:B
B.doIt(); // CLASS:B
B.doIt("Hello") // CLASS:B, message: Hello
}
}
EDIT
You could have a simple controller and a few delegates to do something similar. Although its not all just static, you have alot more control over the parts. Please note that the example is very basic on purpose.
You could also have a class A and a class B and B has A as a dependency and then you just override what A (the delegate) should do differently. Also here ... its not static, but I do not think that this is your main problem. Its more the design after all.
import java.util.*;
public class MyClass {
public static void main(String... args) {
final DoItController doItController = DoItController.Singleton.getInstance();
doItController.doIt(DoerDelegateA.class);
doItController.doIt(DoerDelegateB.class);
doItController.doIt(DoerDelegateComplex.class);
}
}
interface Doer {
void doIt();
}
class DoItController {
final Map<Class<? extends DoerDelegate>, DoerDelegate> doerDelegateMap;
private DoItController() {
doerDelegateMap = new LinkedHashMap();
doerDelegateMap.put(DoerDelegateA.class, new DoerDelegateA());
doerDelegateMap.put(DoerDelegateB.class, new DoerDelegateB());
doerDelegateMap.put(DoerDelegateComplex.class, new DoerDelegateComplex());
}
public void doIt(Class<? extends DoerDelegate> delegateReference) {
final DoerDelegate delegate = doerDelegateMap.getOrDefault(delegateReference, null);
if (delegate != null) {
delegate.doIt();
}
}
public static class Singleton {
private static DoItController instance;
public static DoItController getInstance() {
if (instance == null) {
instance = new DoItController();
}
return instance;
}
}
}
class DoerDelegate implements Doer {
@Override
public void doIt() {
System.out.println("Do something with: " + getClass().getSimpleName());
}
}
class DoerDelegateA extends DoerDelegate {}
class DoerDelegateB extends DoerDelegate {}
class DoerDelegateComplex extends DoerDelegateA {
@Override
public void doIt() {
System.out.println("Override standard method with: " + getClass().getSimpleName());
}
}
or ...
public class MyClass {
public static void main(String... args) {
final B b = new B();
b.doIt();
}
}
interface Doer {
void doIt();
}
class A implements Doer {
@Override
public void doIt() { System.out.println("DO IT"); }
}
class B implements Doer {
final A aDelegate;
public B() {
aDelegate = new A();
}
@Override
public void doIt() {
// standard
aDelegate.doIt();
// your specific B stuff
//...
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论