Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中是否可以在运行时实现接口?_Java_Bytecode - Fatal编程技术网

在Java中是否可以在运行时实现接口?

在Java中是否可以在运行时实现接口?,java,bytecode,Java,Bytecode,我正在从事一个项目,其中有许多对象是由库创建的,我无法访问这些对象的创建过程 下面的代码片段是一个很好的例子来说明我的问题 代码: ExampleInterface是Clazz在编译时可能实现也可能不实现的接口 代码: 下面的代码就是我遇到的问题。注意到以下几点: public interface CustomMouseListener { public void mouseClicked(CustomMouseEvent evt); public void mousePress

我正在从事一个项目,其中有许多对象是由库创建的,我无法访问这些对象的创建过程

下面的代码片段是一个很好的例子来说明我的问题

代码:

ExampleInterface
是Clazz在编译时可能实现也可能不实现的接口

代码:

下面的代码就是我遇到的问题。注意到以下几点:

public interface CustomMouseListener {
    public void mouseClicked(CustomMouseEvent evt);
    public void mousePressed(CustomMouseEvent evt);
    public void mouseReleased(CustomMouseEvent evt);
    //etc
}
  • run()
    仅在c是
    ExampleInterface
    的实例时调用
  • getRunConditions(Clazz c)
    executeClazz(Clazz c)
    都是我无权访问的类中的私有方法
  • 编译时,
    Clazz
    将不包含名为
    run()
    的方法
  • 执行者不是我的班级。我在任何地方都无法访问它 方式(我甚至无法获得该类的实例)
  • 代码:

    显然,下面的方法在语法上是不可能的,但这正是我想要实现的。基本上,如果c尚未实现
    ExampleInterface
    ,请将c设置为实现
    ExampleInterface
    ,然后提供必须重写的方法

    注意到以下几点:

    public interface CustomMouseListener {
        public void mouseClicked(CustomMouseEvent evt);
        public void mousePressed(CustomMouseEvent evt);
        public void mouseReleased(CustomMouseEvent evt);
        //etc
    }
    
  • 扩展接口(
    接口名称
    由语法组成 为了说明我的目标而创建
  • 必须在此处(运行时)定义
    run()
  • 我不能使用包装器或代理类作为解决方案。也就是说,
    Clazz
    对象必须最终实现
    ExampleInterface
    ,我不能使用变通方法。(如果您想知道原因,请参阅
  • 代码:

    为了澄清,我遇到的问题是,我需要始终知道在
    Clazz
    中何时调用
    run()
    。如果
    Clazz
    没有实现
    ExampleInterface
    ,我不知道什么时候应该调用
    run()

    同时,我还想偶尔添加对默认情况下不支持的
    run()
    的支持。因为我没有权限创建
    Clazz
    对象,所以我无法通过自己实现接口来实现这一点

    问题:简单地说,是否可以在运行时实现接口(并提供所需的方法)?

    注意:虽然唯一的解决方案可能需要反射(如果需要,请在下面发布),但我使用的库有一个安全管理器,可以阻止所有反射的使用。也就是说,一个反思性的解决方案在将来可能对其他人有用,但对我没有用处

    而且,我不是说在我自己的程序中只使用一个库。一个已经运行的主机应用程序(我正在使用的库就是为了这个应用程序而创建的)遵循并运行我为它编写的代码。如果该应用程序不喜欢我提供的任何代码(即,与它的安全管理器冲突),则该代码甚至不会被编译

    我为什么需要这样做:

    这与我正在使用的图书馆有关。因为
    ExampleExecutor
    是我无权访问的方法,并且我无法控制Clazz的创建,所以我无法确定何时执行
    run()

    我之所以需要知道何时执行
    run()
    ,是因为实际上,
    run()
    是我正在使用的库的一部分的事件处理程序

    例如:
    mouseClicked(CustomMouseEvent evt)
    可能是接口
    CustomMouseListener
    的一部分。有时,我正在使用的
    Clazz
    实例在单击鼠标时会引起注意(因此会继承
    CustomMouseListener
    ),而其他时候则不会

    Clazz
    实例不同,我总是关心鼠标是否被单击,并且总是需要触发事件

    实际上,
    ExampleInterface
    实际上是以下内容:

    public interface CustomMouseListener {
        public void mouseClicked(CustomMouseEvent evt);
        public void mousePressed(CustomMouseEvent evt);
        public void mouseReleased(CustomMouseEvent evt);
        //etc
    }
    

    执行建议的唯一方法是使用字节码检测。您可以添加一个代理,在加载之前更改要修改的clazz的字节码

    您需要在加载时这样做的原因是,许多JVM不允许您更改字段,有些JVM不允许您在类加载后添加方法

    一个更简单的解决方案是对类进行反编译、修改并再次编译。假设类可以反编译,这将为您节省大量时间和精力

    我正在使用的库有一个安全管理器,它阻止所有反射的使用


    这是一个奇怪的选择,因为您可以在调用库之前设置自己的安全管理器,它不会阻止您做任何事情。

    我认为您不可能想要什么;有,但它们使用反射,并且看起来您不可能访问类加载器(您可以在其中设置自己的即时字节码操作)。

    您可以使用java instrumentation API(强制)使类适应接口。APM、AOP框架和分析器通常使用这种技术在运行时将日志记录和度量代码注入目标类。应用程序直接使用这种技术是非常罕见的。如果我在生产代码中看到这一点,至少会是一个巨大的危险信号

    尽管如此

    鉴于这些分歧:

    package com.sabertiger.example;
    
    public class Clazz {
        public void purr(){
            System.out.println("Hello world");
        }
    
    }
    
    接口

    package com.sabertiger.example;
    
    public interface ExampleInterface {
        void run();
    }
    
    执行人

    package com.sabertiger.example;
    
    public class ExampleExecutor {  
        public static void main(String[] args) {
            Clazz c=new Clazz();
            // Normally a ClassCastException
            ExampleInterface i=(ExampleInterface)(Object)(Clazz) c;
            i.run();
        }
    }
    
    正常运行会产生以下错误:

    Exception in thread "main" java.lang.ClassCastException:
      com.sabertiger.example.Clazz cannot be cast to 
      com.sabertiger.example.ExampleInterface
        at com.sabertiger.example.ExampleExecutor.main(ExampleExecutor.java:7)
    
    您可以通过转换类来提供缺少的接口和实现,从而使其正常工作:

    package com.sabertiger.instrumentation;
    
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.lang.instrument.Instrumentation;
    import java.security.ProtectionDomain;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    
    public class ExampleInterfaceAdapter implements ClassFileTransformer {
    
        public static void premain(String agentArgument, Instrumentation instrumentation) {
            // Add self to list of runtime transformations
            instrumentation.addTransformer(new ExampleInterfaceAdapter());
        }
    
        @Override
        // Modify only com.sabertiger.example.Clazz, return all other unmodified
        public byte[] transform(ClassLoader loader, String className,
                Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                byte[] classfileBuffer) throws IllegalClassFormatException {
    
            if(className.matches("com/sabertiger/example/Clazz")) {
                return addExampleInterface(className, classfileBuffer );            
            } else {
                return classfileBuffer;
            }
        }
    
        // Uses javassist framework to add interface and new methods to target class
        protected byte[] addExampleInterface(String className, byte[] classBytecode) {
            CtClass clazz= null;
            try {
                ClassPool pool = ClassPool.getDefault();
                clazz = pool.makeClass(new java.io.ByteArrayInputStream(classBytecode));
    
                String src=
                  "{         "+
                  "  purr(); "+
                  "}         ";
    
                //Add interface
                CtClass anInterface = pool.getCtClass("com.sabertiger.example.ExampleInterface");
                clazz.addInterface(anInterface);
    
                //Add implementation for run method
                CtMethod implementation = CtNewMethod.make(
                        CtClass.voidType,
                        "run",
                        new CtClass[0],
                        new CtClass[0],
                        src,
                        clazz);
                clazz.addMethod(implementation);
    
                classBytecode=clazz.toBytecode();
            } catch(Throwable e) {
                throw new Error("Failed to instrument class " + className, e);
            }
            return classBytecode;
        }
    
    }
    
    将所有物品装入一个罐子中,使其正常工作:

    jar -tf agent.jar
    META-INF/MANIFEST.MF
    com/sabertiger/instrumentation/ExampleInterfaceAdapter.class
    
    现在我们可以将Clazz传递给ExampleExecutor

    java -javaagent:agent.jar -classpath ..\instrumentation\bin com.sabertiger.example.ExampleExecutor
    Hello world
    

    根据您的java版本,您可以使用lambda表达式(使用Java8)

    代码将相对简单:

    Clazz o = .... // Here you obtain your object through third party library
    ExampleInterface yourInterface = o::run;
    yourInterface.run();
    
    请注意,这仅是w
    jar -tf agent.jar
    META-INF/MANIFEST.MF
    com/sabertiger/instrumentation/ExampleInterfaceAdapter.class
    
    java -javaagent:agent.jar -classpath ..\instrumentation\bin com.sabertiger.example.ExampleExecutor
    Hello world
    
    Clazz o = .... // Here you obtain your object through third party library
    ExampleInterface yourInterface = o::run;
    yourInterface.run();