简体   繁体   中英

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:

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<?>> 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
}

You need to do one of the following:

  • remove default modifier from getValueName()
  • make [abstract] class instead of interface
  • upgrade your platform to Java 1.8

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