Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would I make this Java 7 compatible?

I have an interface that basically looks like this:

public interface ISetting<T> {
    public T getDefault();
    public T value();
    public void set(T value);
    public String getName();

    public default String getValueName() {
        Object obj = value();
        if (obj instanceof Boolean) {
            return (boolean)obj ? "Yes" : "No"; 
        }

        return obj.toString();
    }
}

And then in another class I have a list of ISetting<?>

private List<ISetting<?>> settings = Arrays.asList(
        new ClassMode(),
        new EndMode(),
        new PlayerLives(),
        new JoinMidGame(),
        new ScoreboardDisplay(),
        new LifePerKill(),
        new ExplosiveBullets(),
        new ReloadTime());

And this all works perfectly! However, the platform where I use my code doesn't support Java 8, so I have to use Java 7, and here's where the problems come.

If I set the Maven target to 1.7, like this in my pom.xml:

<configuration>
    <source>1.8</source>
    <target>1.7</target>
</configuration>

Then the code compiles perfectly with no errors or anything. However, when I try to run the code, it gives me this error:

java.lang.ClassFormatError: Method getValueName in class net/uniqraft/murder/match/settings/ISetting has illegal modifiers: 0x1

I tried to Google it but couldn't find anything that I understood or seemed to be applicable in my case.

So, I thought, I'll just make the entire codebase into Java 7:

<configuration>
    <source>1.7</source>
    <target>1.7</target>
</configuration>

The first error I see is:

Default methods are allowed only at source level 1.8 or above

Which is incredibly annoying and I don't know how to bypass that. A lot of my code is dependent on default implementations. I guess I just have to use abstract classes instead?

But the more problematic error I see is on the List<Setting<?>> I have:

Type mismatch: cannot convert from List<ISetting<? extends Object&Comparable<?>&Serializable>> to List<ISetting<?>>

I have no idea what that means or how to fix it. The quickfix Eclipse offers are of no help.

In case the you need to see the full non-stripped ISetting class or the full stacktrace, I put them externally as they're rather spacey:

  • ISetting.java
  • Stacktrace
like image 882
Vapid Linus Avatar asked Apr 19 '15 11:04

Vapid Linus


People also ask

Can program developed with Java 7 be run on Java 8?

Binary Compatibility Except for the noted incompatibilities, class files built with the Java SE 7 compiler will run correctly in Java SE 8. Class files built with the Java SE 8 compiler will not run on earlier releases of Java SE.

Is Java 7 backwards compatible?

The backwards compatibility means that you can run Java 7 program on Java 8 runtime, not the other way around. There are several reasons for that: Bytecode is versioned and JVM checks if it supports the version it finds in .

How do I change my Java version?

In the Java Control Panel, click on the Java tab. Verify that the latest Java Runtime version is enabled by checking the Enabled box. Click OK in Java Control Panel window to confirm changes and close the window. Try to run same applet and verify it is now running using latest version of Java installed in your system.


1 Answers

I will divide the answer in two parts, the first regarding type inference and the second regarding default methods:

Type inference

In Java 7, the type of an expression is the same, regardless of context. So when you do:

Arrays.asList(new ClassMode(), new EndMode(), ...);

It does not create a List<ISetting<?>>. You could make it work by changing the settings type to List<? extends ISetting<?>>. That is, a list that can hold elements that can be a ISetting<?> or any subtype of it:

List<? extends ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);

In Java 8, assigning the resulting list to a List<ISetting<?>> works because of poly expressions. This means that the deduced type of some expressions can be influenced by the target type. So when you do:

private List<ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);

The compiler analyses the target type and implicitly passes a type parameter to Arrays.asList(), which is equivalent to doing:

private List<ISetting<?>> settings = Arrays.<ISetting<?>>asList(new ClassMode(), new EndMode(), ...);

Which creates a List<ISetting<?>> and assigns it to settings. The above form also works in Java 7, if you don't want to change the settings type.

Default methods

Java 7 does not have default methods. Instead, you could create an abstract skeletal implementation to go together with your interface. The interface will define the type, and the skeletal implementation will provide the default functionality.

First, turn the default methods in your interface into regular method declarations:

public interface ISetting<T> {
    T getDefault();
    T value();
    void set(T value);
    String getName();

    // Former default methods:
    String getValueName();
    boolean isHidden();
    boolean isDefault();
    // etc.
}

Then create an abstract class to hold the default implementations:

public abstract class AbstractSetting<T> implements ISetting<T> {

    @Override
    public String getValueName() {
        Object obj = value();
        if (obj instanceof Boolean) {
            return ((Boolean) obj) ? "Yes" : "No";
        }
        return obj.toString();
    }

    @Override
    public boolean isHidden() {
        return false;
    }

    // etc.
}

Now make your concrete classes implement the ISetting<T> interface and extend the AbstractSetting<T> class. For example:

public class ConcreteSetting extends AbstractSetting<Boolean> implements ISetting<Boolean> {
    // concrete implementation
}
like image 51
Anderson Vieira Avatar answered Sep 18 '22 06:09

Anderson Vieira