Suppose I constructed the following class
public enum OptimizationAlgorithmType {
VANILLA(new HashMap<String, Double>()) {
private final static String ETA = "eta";
private Double eta = null;
public OptimizationAlgorithmType setEta(Double eta) {
this.eta = eta;
return this;
}
@Override
public Map<String, Double> getHyperarameters() {
this.map.put(ETA, eta);
return this.map;
}
},
MOMENTUM(new HashMap<String, Double>()) {
private final static String ALPHA = "alpha";
private final static String BETA = "beta";
private Double alpha = null;
private Double beta = null;
public OptimizationAlgorithmType setAlpha(Double alpha) {
this.alpha = alpha;
return this;
}
public OptimizationAlgorithmType setBeta(Double beta) {
this.beta = beta;
return this;
}
@Override
public Map<String, Double> getHyperarameters() {
this.map.put(ALPHA, alpha);
this.map.put(BETA, beta);
return this.map;
}
};
protected Map<String, Double> map;
private OptimizationAlgorithmType(Map<String, Double> map) {
this.map = map;
}
public abstract Map<String, Double> getHyperarameters();
}
My goal was to construct an API that when I select a specif Enum then different methods would be available. For example
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward)
.setNumberThreads(10)
.setBatch(5)
.setEpochs(100)
.setOptimizer(OptimizationAlgorithmType.VANILLA.setEta(0.001));
Or
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward)
.setNumberThreads(10)
.setBatch(5)
.setEpochs(100)
.setOptimizer(OptimizationAlgorithmType.MOMENTUM.setAlpha(0.01).setBeta(0.99));
Unfortunately, this is not allowed. The ide warns about the unused methods (ie: setEta()
)- and the methods are not available at all to select from the specific enum.
Is there a trick I can use to get the desired API?
Thanks
Edit Added an alternative answer below
I came up with some better
First an interface, then the two parameter classes that implement the interface
public interface Hyperparameter {...}
public enum VanillaParameter implements Hyperparameter {
ETA {
@Override
public Double getValue() {
return this.etaValue;
}
@Override
public void setValue(Double value) {
if (value == null) {
throw new IllegalStateException("Parameter value cannot be set to null!");
}
this.etaValue = value;
}
};
protected Double etaValue = 0.005;
}
public enum MomentumParameter implements Hyperparameter {
...
}
Now add the main enum class
public enum OptimizationType {
VANILLA{
private VanillaParameter eta = VanillaParameter.ETA;
@Override
public Hyperparameter get(Hyperparameter parameter) {
switch ((VanillaParameter) parameter) {
case ETA: return this.eta;
}
return null;
}
@Override
public OptimizationType set(Hyperparameter parameter, Double value) {
switch ((VanillaParameter) parameter) {
case ETA:
this.eta.setValue(value);
break;
}
return OptimizationType.VANILLA;
}
},
...
}
And the API looks really nice in my opinion and resembles my initial intention. Look forward to suggestions
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation.BackpropagationBuilder(feedForward)
.setBatch(5)
.setEpochs(100)
.setThreads(10)
.setOptimizer(OptimizationType.MOMENTUM
.set(MomentumParameter.ALPHA, 0.001)
.set(MomentumParameter.BETA, 0.92))
.build();
maybe try something like:
import java.util.Arrays;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
public class So54067082enums_to_implement_different_methods_in_java {
enum Type {
vanilla(new TreeSet<>(Arrays.asList(new String[] {"eta"}))),
momentum(new TreeSet<>(Arrays.asList(new String[] {"alpha","beta"})));
Type(Set<String> names) {
this.names=names;
}
final Set<String> names;
}
static class ParameterSet {
ParameterSet(Type type) {
this.type=type;
}
Double get(String name) {
if(type.names.contains(name))
return map.get(name);
else throw new RuntimeException("oops");
}
void set(String name,Double value) {
if(type.names.contains(name))
map.put(name,value);
else throw new RuntimeException("oops");
}
@Override public String toString() {
return "ParameterSet [type="+type+", map="+map+"]";
}
final Type type;
private final SortedMap<String,Double> map=new TreeMap<>();
}
public static void main(String[] args) {
ParameterSet parameterSet=new ParameterSet(Type.vanilla);
parameterSet.set("eta",.01);
System.out.println(parameterSet);
ParameterSet parameterSet2=new ParameterSet(Type.momentum);
parameterSet2.set("alpha",.1);
parameterSet2.set("beta",.9);
System.out.println(parameterSet2);
}
}
I don't see why you couldn't just use a less naive approach to the problem:
class OptimizationAlgorithm {
public static class Vanilla implements OptimizationAlgorithm {
public Vanilla setAlpha(float a) {
return this;
}
public Vanilla setBeta(float a) {
return this;
}
}
public static Vanilla vanilla() { return new Vanilla(); }
}
backpropagation.setOptimizer(OptimizationAlgorithm.vanilla().setAlpha(0.05f).setBeta(0.10f));
A lot less boilerplate code, no mutable static instances problem and basically the same interface from the outside.
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.