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:
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.
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 .
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.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With