Enum with generic base class passed as contructor parameter - java

I'm attempting to create an enum whose constructor accepts an object whose base class is a generic class.
I seem to be unable to fetch the underlying generic type from within the enum however, Object gets returned instead of T.
Is there a way to do this?
abstract public class Field<T> {
abstract public T get();
}
public class IntegerField extends Field<Integer> {
public Integer get() {
return 5;
}
}
public class StringField extends Field<String> {
public String get() {
return "5";
}
}
public enum Fields {
INTEGER (new IntegerField()),
STRING (new StringField());
private final Field<?> field; // <<--- I can't have Field<T>, enum's can't be generic. :(
<T> Fields(Field<T> field) {
this.field = field;
}
public <T> T get() {
return field.get(); // <<--- Returns Object, not T
}
}

The issue is that enums can't be generically typed so even if you cast that get call ((T) field.get()) you won't have type safety because it will agree with any assignment (you could compile this successfully for instance: boolean b = Fields.INTEGER.get()).
Just use constants instead:
public final class Fields {
public static final Field<Integer> INTEGER = new IntegerField();
public static final Field<String> STRING = new StringField();
}

Why do you think an enum is preferable to this?
public final class Fields {
public static final Field<Integer> INTEGER = new IntegerField();
public static final Field<String> STRING = new StringField();
//private ctor
}
or if you prefer
public final class Fields {
public static Field<Integer> integerField() {
return new IntegerField();
}
public static Field<String> stringField() {
return new StringField();
}
//private ctor
}
Why would I want to call Fields.INTEGER.get() when I can just use Fields.INTEGER?

Related

Use getter as parameter in method

I have multiple different classes Class1, Class2, Class3.
Each class has different variables and getters for them.
public class Class1 {
private String var1_1;
private String var1_2;
private String var1_3;
public String getVar1_1() { return var1_1;}
public String getVar1_2() { return var1_2;}
public String getVar1_3() { return var1_3;}
}
public class Class2 {
private String var2_1;
private String var2_2;
private String var2_3;
public String getVar2_1() { return var2_1;}
public String getVar2_2() { return var2_2;}
public String getVar2_3() { return var2_3;}
}
public class Class3 {
private String var3_1;
private String var3_2;
private String var3_3;
public String getVar3_1() { return var3_1;}
public String getVar3_2() { return var3_2;}
public String getVar3_3() { return var3_3;}
}
How can I write method which takes List of objects and getters(functions) as a method parameters?
For example:
List<Class1> list1 = //doesn't matter
List<Class2> list2 = //doesn't matter
List<Class3> list3 = //doesn't matter
generateRows(list1, Class1::getVar1_1, Class1::getVar1_3);
generateRows(list2, Class2::getVar2_1, Class2::getVar2_2, Class2::getVar2_3);
generateRows(list3, Class3::getVar3_1, Class3::getVar3_2);
Method
public void generateRows(List<T> list, /*???Something???*/... getters) {
for(T object: list) {
/*How to use getters to print?*/
/*System.out.println(obj.firtsGetter())*/
}
}
What I should write instead of ???Something???. Something like Function<T, String> or Consumer<T>? And how can I use getters in method?
You need to wrap the method calls in a Function. A Consumer, or another FunctionalInterface might do the trick as well, depending on your needs.
Then you need to have your method accept varargs of this function.
public static <T> void generateRows(List<T> list, Function<T, String>... getters) {
for (T object : list) {
for (Function<T, String> getter : getters) {
System.out.println(getter.apply(object));
}
}
}
Function will accept argument of type T and return a String. T can be anything - Class1, Class2, etc., as long you can return a string from this object - that means to have a getter method returning String in your case.
First you can defined a interface eg:
interface Get{
String getVar1();
String getVar2();
String getVar3();
}
Second All of classes implemente it:
class Class2 implements Get{
private String var2_1;
private String var2_2;
private String var2_3;
public String getVar1() { return var2_1;}
public String getVar2() { return var2_2;}
public String getVar3() { return var2_3;}
}
Third:
public void generateRows(List<T> list, Get ...getters) {
for(T object: list) {
/*How to use getters to print?*/
/*System.out.println(obj.firtsGetter())*/
}
}

How to initialize a value when creating an object in a static block

When creating the Racing class in ApplicationTest, I want to hand over the FixNumberBehavior class to the argument.
As shown below, to pass the argument to initialize FixNumberBehavior, but cannot pass the class field value to the static block.
The error message is as follows.
Variable 'isMove' might not have been initialized
FixNumberBehavior.java
public class FixNumberBehavior implements CarMoveBehavior {
private final boolean isMove;
private static FixNumberBehavior fixNumberBehavior;
static {
fixNumberBehavior = new FixNumberBehavior(); //error
}
public FixNumberBehavior(final boolean isMove) {
this.isMove = isMove;
}
#Override
public boolean moveBehavior() {
return isMove;
}
}
Racing.java
public class Racing {
private List<Car> cars;
private CarMoveBehavior carMoveBehavior;
public Racing(List<Car> cars, final CarMoveBehavior carMoveBehavior) {
this.cars = cars;
this.carMoveBehavior = carMoveBehavior;
}
public List<Car> getCars() {
return cars;
}
public void drive() {
cars.stream()
.forEach(car -> racingCondition(car));
}
private void racingCondition(Car car) {
if (carMoveBehavior.moveBehavior()) {
car.moveForward();
}
}
}
ApplicationTest
#ParameterizedTest
#CsvSource({"a,aa,aaa"})
void fixRandomNumberTest(String one, String two, String three) {
final List<Car> cars = Arrays.asList(new Car(one), new Car(two), new Car(three));
Racing racing = new Racing(cars, new FixNumberBehavior(true));
racing.drive();
racing.drive();
assertAll(
() -> assertThat(cars.get(0).getStep()).isEqualTo(2),
() -> assertThat(cars.get(1).getStep()).isEqualTo(2),
() -> assertThat(cars.get(2).getStep()).isEqualTo(2)
);
}
How can I initialize an object in the static block?
The problem is FixNumberBehavior has a final field that must be set in the constructor, or in an assignment on the field definition line.
While there is a constructor that takes a value for that field, the static block is not using that constructor, but instead a no-arg constructor.
Pass the value for that final field (isMove) in the new statement.
I am not sure why you want to overcomplicate things by
providing no-argument constructor when you already have constructor in which you let client decide if created instance of FixNumberBehavior will set isMove to true or false.
changing (in your answer) isMove from being final to being static. Those two keywords have different purpose:
final prevents reassigning new value to it
static makes this field a class field, not instance field, so even if you create two instances of FixNumberBehavior there will be only one isMove variable which they both will use (so you can't preserve in one instance state like isMove=true and in other state isMove=false).
What you are looking for is probably simply
public class FixNumberBehavior implements CarMoveBehavior {
private final boolean isMove;
private static FixNumberBehavior fixNumberBehavior = new FixNumberBehavior(true);
//set value you want here ^^^^
public FixNumberBehavior(final boolean isMove) {
this.isMove = isMove;
}
#Override
public boolean moveBehavior() {
return isMove;
}
}
I solved it by attaching static to the field.
Objects created in the static block are not identified when compiling. Therefore, the argument value to be transferred to the object you create in the static block must also be processed statistically.
package racingcar.model.domain;
public class FixNumberBehavior implements CarMoveBehavior {
private static boolean isMove;
private static FixNumberBehavior fixNumberBehavior;
static {
fixNumberBehavior = new FixNumberBehavior(isMove);
}
private FixNumberBehavior() {
}
public static FixNumberBehavior getInstance(){
return fixNumberBehavior;
}
public FixNumberBehavior(final boolean isMove) {
this.isMove = isMove;
}
#Override
public boolean moveBehavior() {
return isMove;
}
}

Abstract class constructor param vs. abstract method for final data

What are the pros/cons of using the abstract class constructor vs. an abstract method for passing final data to an abstract class?
Pass via constructor:
public abstract class MyAbstractClass<T> {
private final String type;
private final Function<String, T> factoryFn;
protected MyAbstractClass(String type, Function<String, T> factoryFn) {
this.type = type;
this.factoryFn = factoryFn;
}
public T doSomething(String value) { ... }
}
Pass via abstract method:
public abstract class MyAbstractClass<T> {
abstract String getType();
abstract T getFactoryFn(String value);
public T doSomething(String value) { ... }
}
I'm aware that the abstract methods can potentially be misused, because it doesn't enforce to always return the same value.
But apart from that, is it just a matter of personal preference, or are there any real (dis)advantages for using one over the other?
I hope I am understanding your question correctly..
Usually, when a property of a class is always held in a field, it is more concise to use an abstract constructor. For example, consider the two following scenarios....
// Scenario 1:
abstract class AClass {
final int field;
public AClass(int f) {
field = f;
}
public int getField() {
return field;
}
}
class Class1 extends AClass {
public Class1(int f) {
super(f);
}
// Class Unique Code...
}
class Class2 extends AClass {
public Class2(int f) {
super(f);
}
// Class Unique Code...
}
// Scenario 2:
abstract class AClass {
public abstract int getField();
}
class Class1 extends AClass {
final int field;
public Class1(int f) {
field = f;
}
#Override
public int getField() {
return field;
}
// Class Unique Code...
}
class Class2 extends AClass {
final int field;
public Class2(int f) {
field = f;
}
#Override
public int getField() {
return field;
}
// Class Unique Code...
}
Scenario 1 is shorter since the getter logic for field only needs to be specified once. Whereas in scenario 2, the getter logic must be overridden by both subclasses. I find scenario 2 to be redundant... why write the same code twice when you can use java inheritance to your advantage.
As a final note, I usually don't hold functions in fields unless totally necessary. Whenever you have a function in a field, it's usually a sign that an abstract function can be applied.
Here is your original code with my advice applied...
public abstract class MyAbstractClass<T> {
private final String type;
protected MyAbstractClass(String t) {
type = t;
}
protected abstract T applyFactoryFunction(String value);
public T doSomething(String value) { ... }
}
Hope this helped!

Map with enum key and different value types

I want to define map in Java, which keys are enums, and types of value depend of key. For example, suppose that we have following enum type:
enum KeyType {
HEIGHT(Integer.class),
NAME(String.class),
WEIGHT(Double.class)
// constructor and getter for Class field
}
and some map:
Map< KeyType, Object > map = new EnumMap<>(KeyType.class);
Is there any simple and safe way to write generic method:
public < T > T get(KeyType key) {
//...
}
that would get value from that map and cast it to corresponding with type class?
UPDATE!!!:
With this in mind:
enum KeyType {
//your enums ...
private final Class val;
//constructor ...
//and generic(!) access to the class field:
<T> Class<T> val() {
return val;
}
}
...this is possible:
public <T> T get(KeyType key) {
return (T) key.val().cast(map.get(key));
}
Your map definition would need to be
Map< KeyType, ?> map = new EnumMap<>(KeyType.class);
If you specify Object as a generic type, only actual instances of Object are allowed, not sub-types.
I don't believe there's any straight forward, generic way (no pun intended) to do what you want. You would need to create some mapping function that translates the object to the correct type based on the enum.
You can't do it with enums. But you could write a "fake" enum (the way Java code did it before Java 1.5, with private constructors and public static instances), and attach a generic type to each constant:
import java.io.Serializable;
import java.util.Map;
public final class KeyType<T>
implements Serializable {
private static final long serialVersionUID = 1;
public static final KeyType<Integer> HEIGHT =
new KeyType<>("HEIGHT", Integer.class);
public static final KeyType<String> NAME =
new KeyType<>("NAME", String.class);
public static final KeyType<Double> WEIGHT =
new KeyType<>("WEIGHT", Double.class);
private static final KeyType<?>[] allValues = {
HEIGHT, NAME, WEIGHT
};
/** #serial */
private final String name;
/** #serial */
private final Class<T> type;
private KeyType(String name,
Class<T> type) {
this.name = name;
this.type = type;
}
public String name() {
return name;
}
public Class<T> getType() {
return type;
}
#Override
public String toString() {
return name();
}
public static KeyType<?>[] values() {
return allValues.clone();
}
public static KeyType<?> valueOf(String name) {
for (KeyType<?> value : allValues) {
if (value.name.equals(name)) {
return value;
}
}
throw new IllegalArgumentException("No such value: \"" + name + "\"");
}
#Override
public boolean equals(Object obj) {
return (obj instanceof KeyType &&
this.name.equals(((KeyType<?>) obj).name));
}
#Override
public int hashCode() {
return name.hashCode();
}
public T getValue(Map<KeyType<?>, ?> map) {
return type.cast(map.get(this));
}
}

How to limit generic class parameter to certain classes

In my generic class I need to restrict type parameter to Integer OR String. Is there a way to achieve this? I cannot use T extends SomeClass to limit types, because common parent is just Object...
update
public abstract class MyClass<T>{
private T value;
public T getValue(){
return value;
}
}
I'd like the value type to be a String or an Integer and I need to use the same method to get it (not getIntValue() + getStringValue() )
This doesn't seem to help...
If I were you, I would overload two methods:
public void withInteger(Integer param) { .. }
public void withString(String param) { .. }
Note that there's no reason to use something like T extends String, because both String and Integer are final and can't be subclassed.
Just made your class ctor private and pass through a factory method to create implementation; type restriction is not bounded to MyClass but via factory.
class MyClass<T> {
private T value;
MyClass(T value) { this.value = value; }
public T getValue() { return value; }
}
class MyClassFactory {
public final static MyClass<Integer> createInteger(Integer i) {
return new MyClass<Integer>(i);
}
}

Categories

Resources