简体   繁体   中英

java inlining final immutable constants

Is it possible to forbid inlining of final Strings? Probably that is a weird requirement but I need to change final field value in runtime. It is necessary for unit testing.

For example:

class asd {
    public static final String value = "sdfmsdkofl";

    public String getValue() {
        return value;
    }
}

I need somehow to avoid inlining of value variable. And I can not change source code :(. That means I can not change way of field is accessed, add getters and setters.

Some magical parameters that would switch off ALL optimisations, even such simple.

You can't. The only possible workaround is to make the constant not final , which makes it, well, not a constant. What you should be doing is reorganizing your code so it can be tested without this sort of madness.

Hm. First caevat: this is a BAD thing to do in production code. I've done it in tests (to switch jdbc driver) but it is not a good idea. But: this is probably doable if you do it early enough. JavaC (assuming you use the Oracle one) does no optimization, it's all done by the the JIT. So, if you change it before the JIT does its magic then it should be fine. In theory,you should be able to change the value after run-time optimization as well, since the JIT:ed code should be marked as no longer valid once you change the string but here I'm skating on very thin ice indeed.

This is (parts) of my test code.

I need to change the driver in this class:

class MysqlConnection {
   private static final String DRIVER_NAME = "com.mysql.jdbc.Driver";

    protected Connection instantiateNewConnection() {
    try {
        // The newInstance() call is a work around for some 
        // broken Java implementations

        Class.forName(DRIVER_NAME).newInstance();
    } catch (Exception ex) {
        // handle the error 
        LOG.info("Class Exception: " + ex);
    }
}
}

And I do it like this:

class DBOperation {
    static {
        Field f = MysqlConnection.class.getDeclaredField("DRIVER_NAME");
        f.setAccessible(true);
        f.set(f, LocalFlipper.HSQLDB_DRIVER);
    }
}

This works. It IS possible to change final fields in java, it's just not a good idea. First I modify the field in question, then I instantiate an instance and the DRIVET_NAME field contains the jdbc driver I want.

There seems to be some doubts about wether this will work or not, but I can assure you it does, try it out for yourselves.

@LouisWasserman: I've gone and javap:ed parts of the code:

Class.forName(DRIVER_NAME).newInstance();

corresponds to

   28:  ldc     #16; //String com.mysql.jdbc.Driver
   30:  invokestatic    #17; //Method  java/lang/Class.forName(Ljava/lang/String;)Ljava/lang/Class;
   33:  invokevirtual   #18; //Method java/lang/Class.newInstance:()Ljava/lang/Object;

As you can see, the String is not inlined. Besides, how do you inline an object (or complex type)? You can inline a reference (naturally). I agree that if we have code like

Class.forName("com.mysql.jdbc.Driver");

Then there is no way to access that string since we can't get a reference to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM