Java share common functionality between enums using interfaces - java

I have a few enums that have the same functionality but contain different constants for organization reasons. They each look like this:
public enum OneEnum {
greenApple(apple, green),
redApple(apple,red);
private final String fruit;
private final String type;
private OneEnum (final String fruit, final String type) {
this.fruit = fruit;
this.type = type;
}
public String getFruit() {
return fruit;
}
public String getType() {
return type;
}
}
The other enums have the same private fields, constructor, and methods and only differ by listed constants. I was wondering how much of this code I can move to a common place such as interface (or abstract class if possible). I am able to create an interface such as:
public interface CommonEnum {
String getFruit();
String getType();
}
but can I do better than that?

You could do something like this:
public interface CommonEnum { ...
public class Holder implements CommonEnum() {
private final String fruit ...
}
public enum OneEnum {
GREEN_APPLE(new Holder(GREEN, APPLE)), ...
private OneEnum(Holder holder) { this.holder = holder };
public getHolder() { return holder };
In other words: if that "combination" of values is the "common thing" that you need in different places, than that should go into its own class.
Of course, the downside of this is: users of your enum would now be violating the Law of Demeter, as in:
Fruit fruit = OneEnum.GREEN_APPLE.getHolder().getFruit();

I don't at all see the point in using an enum for the combination of values at all.
What you have in effect is
public enum Fruit {
APPLE, ORANGE, BANANA //...
}
public enum Color {
RED, GREEN, BLUE // ...
}
and a combination of the two
class ColoredFruit {
public final Fruit fruit;
public final Color color;
public ColoredFruit(Fruit f, Color c) { fruit = f; color = c; }
}
The enums you have are only instances
Collection<ColoredFruit> apples = Arrays.asList(new ColoredFruit(APPLE, RED), new ColoredFruit(APPLE, GREEN));
And if you do feel fancy, add interfaces
interface Fruit {
FruitEnum getFruit();
}
interface Colored {
Color getColor();
}
which ColoredFruit can implement by adding getters.

Related

Should I keep an enum attribute when it has always the same value as a result of a new inheritance?

I have these classes:
enum Brand {
FORD, FERRARI, TESLA, RENAULT;
}
public class Car {
Brand brand;
String plate;
...
}
//getters and setters
Imagine that for some reason, I need to make Car a superclass for two new classes: CombustionCar and ElectricCar. One of the new requierements is that ElectricCar's brand attribute must be always TESLA value and not any of the other ones values.
I've thougth some solutions:
I could keep Brand attr on superclass Car, and make ElectricCar constructor to set TESLA brand. But this way could allow me to set a new Brand after creating the object
public class ElectricCar extends Car {
public ElectricCar(...){
super(Brand.TESLA, ...);
}
ElectricCar ec = new ElectricCar(...);
ec.setBrand(Brand.FORD);
I can take Brand attr out from superclass and set it on both subclasses, but setting it in ElectricCar as a class attribute with a final so anyone would be able to set a new value
public class ElectricCar extends Car {
public static final Brand brand = Brand.TESLA;
...
}
public class CombustionCar extends Car {
private Brand brand;
...
}
Avoid inheritance and use composition, but with this I wont be able to use, for example, a List which contain both:
public class ElectricCar {
private Car car;
private Brand brand = Brand.TESLA;//with no setter
...
}
public class CombustionCar {
private Car car;
private Brand brand;
...
}
I'm asking for the most elegant and manteinable solution, I think any of them would be nice to resolve my problem.
Your first solution is incorrect given that you required a non editable BRAND for an electric car.
Your second solution just doesn't work at all excepted if you override both getter and setter of brand field to use your static field, which is not "elegant and mantainable"
Your third solution doesn't make use of object oriented concept.
A simple solution I would use is to let the field brand and its getter in Car superclass, but I'd only define the setter in the CombustionCar class.
Alternatively, if you extend your model, you could create an intermediate abstract superclass "FreeBrandCar" which implements the setter.
Solution with the setter in CombustionCar
abstract public class Car {
protected String brand;
protected Car(final String b) {
this.brand = b;
}
public String getBrand() {
return this.brand;
}
}
public class ElectricCar extends Car {
public ElectricCar() {
super("Tesla");
}
}
public class CombustionCar extends Car {
public CombustionCar(final String b) {
super(b);
}
public void setBrand(final String b) {
this.brand = b;
}
}
Solution with an intermediate class
abstract public class Car {
protected String brand;
protected Car(final String b) {
this.brand = b;
}
public String getBrand() {
return this.brand;
}
}
abstract public class FreeBrandCar extends Car {
public FreeBrandCar (final String b) {
super(b);
}
public void setBrand(final String b) {
this.brand = b;
}
}
public class ElectricCar extends Car {
public ElectricCar() {
super("Tesla");
}
}
public class CombustionCar extends FreeBrandCar {
public CombustionCar(final String b) {
super(b);
}
}
It respects your requirements :
public void test() {
ElectricCar ec = new ElectricCar();
ec.setBrand("..."): // Doesn't compile
CombustionCar cc = new CombustionCar("Ford"); // OK
cc.setBrand("Fiat"); // OK
Arrays.asList(ec, cc)
.stream()
.forEach(car -> System.out.println(car.getBrand())); // prints Tesla and Fiat
}

Using Non-Static Methods as Enum Properties with Generics

I'll try to keep this short. I'm trying to do something like this:
public enum Fruit {
APPLE("Apple", appleHelper::doAppleThing),
ORANGE("Orange", orangeHelper::doOrangeThing);
private String name;
private Function<String, List<T>> fruitFunction;
Fruit(String name, Function<String, List<T>> fruitFunction) {
this.name = name;
this.fruitFunction = fruitFunction;
}
public String getName() {
return name;
}
public <T> List<T> applyFruitFunction(String someString) {
return fruitFunction.apply(someString);
}
}
Such that later, I can have a method like
private <T> List<T> doFruitThing(String someString, Fruit fruit) {
List<T> transformedFruits = fruit.applyFruitFunction(someString);
if (transformedFruits.isEmpty()) {
throw new FruitException("There was no fruit of type " + fruit.getName());
}
return transformedFruits;
}
There's two problems I'm running into here.
doAppleThing and doOrangeThing are not static methods, and ideally will stay that way, and I can't find any way of creating a local instance of appleHelper and orangeHelper to make the method reference work.
Even if I were to make the methods static, enums can't have Type parameters, so there's no way to have Function<String, List<T>> fruitFunction as a field.
Is there a way this can be done? Or a better approach to this?
Enum values can have their own method implementations. So I would write this as:
public enum Fruit {
APPLE("Apple") {
private final AppleHelper helper = new AppleHelper();
#Override
public <T> List<T> applyFruitFunction(String someString) {
return helper.doAppleThing(someString);
}
},
ORANGE("Orange") {
private final OrangeHelper helper = new OrangeHelper();
#Override
public <T> List<T> applyFruitFunction(String someString) {
return helper.doOrangeThing(someString);
}
};
private String name;
Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract <T> List<T> applyFruitFunction(String someString);
}
However, if you get to the point of needing per-instance state for enum instances, the thing you have is less and less an enum and more of just an abstract base class. It might be better to look into a more OO approach, using a factory/flywheel pattern for example, rather than being tied to a pure enum for this sort of thing. (It's hard to tell for sure because the code in the question is obviously just a simplified example.)

Java Inheritance: Restrict List to Subclass Objects

Let's assume I have 3 classes: Car, Convertible and Garage.
Car:
public class Car {
private String name;
private String color;
public Car(String name, String color) {
this.name = name;
this.color = color;
}
//Getters
}
Convertible inherits from Car:
public class Convertible extends Car{
private boolean roof;
public Convertible(String name, String color, boolean roof) {
super(name, color);
this.roof = roof;
}
public boolean isRoof() {
return roof;
}
}
Garage stores a list of Cars:
public class Garage {
private int capacity;
private List<Car> cars = new LinkedList<Car>();
//Setter for capacity
}
How could I create a subclass of Garage called ConvertibleGarage that can only store Convertibles?
You could use a little bit of generics:
public class Garage<T extends Convertible> {
private int capacity;
private List<T> cars = new LinkedList<T>();
public Garage(int capacity) {
this.capacity = capacity;
}
}
This means when you instantiate a Garage you now have to include a parameter type that is a Convertible or child of it.
Garage<Convertible> cGarage = new Garage<>();
Generics will help here.
Make the Garage class generic Garage<T extends Car>, where a T is a car type it can store. Rewrite the cars list to a generic view List<T> as well.
Then, a Garage<Convertible> is going to be your "ConvertibleGarage".
You don't really need to make a second Garage class, you can use Generics :
public class Garage<T extends Car> {
private int capacity;
private List<T> cars;
public Garage() {
this.cars = new LinkedList<>();
}
public static void main(String[] args) {
Garage<Convertible> garConv = new Garage<>();
garConv.cars.add(new Convertible("", "", true));
Garage<Car> garCar = new Garage<>();
garCar.cars.add(new Car("", ""));
}
}
With this only class you can have a garage for car and a one for convertible
As the other answers have explained, you solve your problem by making your Garage class generic - and therefore allowing any instance of Garage to deal with exactly one kind of cars.
But what is missing so far: this is not "an option" to solve your problem - this is simply "the way to go here". Your idea of using inheritance is "plain wrong". Meaning: when people start with object oriented design, they assume that inheritance is the answer to everything. And actually that is not true. You are rather careful about creating an extends relation between two classes.
And especially when talking about containers - classes that "contain" other objects - then generics is your first thought!

How can I ensure an argument is a class field (Java)?

I am creating an API that returns a list of Cars. The API user must be able to request that the list be filtered and sorted by a certain attribute (field) of the Cars class. How can I do that?
class Car {
public final String model;
public final String color;
public Car(String m, String c) {
model = m;
color = c;
}
}
class CarListRequest {
public final String sortBy;
public final String filterBy;
public final List<String> filterList;
public CarListRequest(String s, String f, List<String> list) {
sortBy = s;
filterBy = f;
filterList = list;
}
}
Is there a way to restrict, using Java language features, that sortBy and filterBy Strings cannot contain any other values than attributes (fields) of the Car class?
I know that I could use an enum to declare all attributes of Car however, that causes a duplication of Strings which I would like to avoid.
#hmc_jake 's reflection suggestion is quite valid. However, if you want to avoid reflection, you could do it using a class hierarchy:
class CarAttribute {
private String attrib;
public CarAttribute(String att){
attrib = att;
}
// add getters and/or setters for attrib ...
}
class CarModel extends CarAttribute {
}
class CarColor extends CarAttribute {
}
class Car {
public final CarModel model;
public final CarColor color;
public Car(CarModel m, CarColor c) {
model = m;
color = c;
}
}
class CarListRequest {
public final CarAttribute sortBy;
public final CarAttribute filterBy;
public final List<CarAttribute> filterList;
public CarListRequest(CarAttribute s, CarAttribute f, List<CarAttribute> list) {
sortBy = s;
filterBy = f;
filterList = list;
}
}
Using Reflection in Java, it is possible to inspect a class's fields.
When, for example, s is passed in, you can perform a check on the argument like so:
for (Field field : Car.class.getFields()) {
if (field.getName().equalsIgnoreCase(s)) {
Do something here to signal that s
was a valid Field of the Car class.
}
}
Doing this allows you to reflectively inspect the Car class in order to verify that the argument passed in is in-fact a field of that class.
Note, however, that if possible you should go with the enum or class hierarchy as reflection might be a little bit overkill for what you're trying to accomplish.

Is it possible to use Enum constants as templates for instantiable classes in Java?

Sorry if my question is a bit unclear; I'm finding it a little tough to find the wording. I spent several hours screwing around in Eclipse, cruising the JavaDoc, and Google, as well as SO. I learned a lot, but didn't find an answer.
What I'd like to be able to do is define an Enum, eg.:
public enum Animals {
Cow,
Chicken,
Sheep,
Horse;
}
and have each enum constant define an instantiable class that's not a local class. Would the following work? And if not, why, and what would?
In some file:
abstract public class Animal {
private String nameString;
public String getName() {
return nameString;
}
}
In another:
public enum Animals {
Cow ((new Animal() {
private boolean hasMilk;
{
nameString = "Cow";
hasMilk = false;
}
public boolean hasMilk() {
return hasMilk;
}
}).getClass()),
Chicken ((new Animal() {
private boolean hasFeathers;
{
nameString = "Chicken";
hasFeathers = true;
}
public boolean hasFeathers() {
return hasFeathers;
}
}).getClass()),
Sheep ((new Animal() {
private boolean isShorn;
{
nameString = "Cow";
isShorn = false;
}
public boolean isShorn() {
return isShorn;
}
public void Shear() {
isShorn = true;
}
}).getClass()),
Horse ((new Animal() {
{
nameString = "Cow";
}
}).getClass());
private Class<? extends Animal> animalClass;
private Animals(Class<? extends Animal> a) {
animalClass = a;
}
public Class<? extends Animal> getAnimalClass() {
return animalClass;
}
}
And then, in some other method of some other class, be able to do this:
Animal farmAnimal;
farmAnimal = Animals.Sheep.getAnimalClass().newInstance();
boolean shorn = farmAnimal.isShorn();
(The value of shorn being false at this point);
farmAnimal.shear();
shorn = farmAnimal.isShorn();
(shorn == true)
farmAnimal = Animals.Sheep.getAnimalClass().newInstance();
shorn = farmAnimal.isShorn();
(shorn == false)
Obviously, this isn't the best way to do what I've done here, but that's not the point. I know I can specify behaviour for enum constants, but that doesn't make them multiply-instantiable as distinct instances. I want to be able to create multiple instances (or copies) of various enum constants, with different instance variables (and different quantities/types of instance variables), with different accessor methods, which I can then do stuff to (alter instance variables) without modifying the enum constant.
I get that enum constants are designed to be immutable. That doesn't clash with my idea; I want each enum constant to represent an immutable definition of a mutable class.
You can do something like this:
public enum AnimalType {
COW {
#Override
public Animal createNew() {
return new Cow();
}
},
HORSE {
#Override
public Animal createNew() {
return new Horse();
}
};
public abstract Animal createNew();
}
public abstract class Animal {
private final AnimalType type;
private final String nameString;
public Animal(final AnimalType type, final String nameString) {
super();
this.type = type;
this.nameString = nameString;
}
public String getName() {
return nameString;
}
public AnimalType getType() {
return type;
}
}
public class Horse extends Animal {
public Horse() {
super(AnimalType.HORSE, "Horse");
}
}
public class Cow extends Animal {
private boolean milk;
public Cow() {
super(AnimalType.COW, "Cow");
}
public boolean hasMilk() {
return milk;
}
public void setMilk(final boolean milk) {
this.milk = milk;
}
}
#Test
public void testEnum() {
Cow cow = (Cow) AnimalType.COW.createNew();
Horse horse = (Horse) AnimalType.HORSE.createNew();
System.out.println("cow : " + cow.getType() + ", has milk: " + cow.hasMilk());
System.out.println("horse: " + horse.getType());
}

Categories