Refactoring by passing generic method - java

I have simplified my code to this:
static private String waitForString(String expected, int attempts) {
String actual = null;
for (int i = 0; i < attempts; i++){
actual = getString();
if (validateString(actual, expected)) {
return actual;
}
}
return null;
}
static private int waitForInt(int expected, int attempts) {
int actual = 0;
for (int i = 0; i < attempts; i++){
actual = getInt();
if (validateInt(actual, expected)) {
return actual;
}
}
return 0;
}
Since I'm using the same loop (and since I have more than one class with more than one corresponding "getter" method and validation method) I would like to refactor it. I tried this:
static <T> T helperMethod(Method getMethod, Method validator,T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = method.invoke(null);
if (validator.invoke(null, actual, expected)) {
return actual;
}
}
return null;
}
However I'm getting following errors:
actual = method.invoke(null);
error: incompatible types: Object cannot be converted to T
validator.invoke(null, actual, expected)
error: incompatible types: Object cannot be converted to boolean
Can I specify in the function declaration only to accept methods with the correct return type? If so, how?
Ideas for other ways to refactor will be appreciated.
EDITED
To make it clear, I wasnt asking how reflect the return type of the method.
Thanks you VGR for the solution.

Do not use reflection.
Reflection is slower, hard for a developer (including yourself) to follow, and cannot be checked by the compiler for correct arguments and return type.
The correct way to accomplish the equivalent of a “pointer to a method” in Java is to wrap the various method calls in a common interface. As of Java 8, as Markus Benko pointed out, you should use suppliers and predicates:
static <T> T waitForValue(Supplier<T> getMethod, BiPredicate<T, T> validator, T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = getMethod.get();
if (validator.test(actual, expected)) {
return actual;
}
}
return null;
}
private static String waitForString(String expected, int attempts) {
return waitForValue(ThisClass::getString, ThisClass::validateString, expected, attempts);
}
private static int waitForInt(int expected, int attempts) {
return waitForValue(ThisClass::getInt, ThisClass::validateInt, expected, attempts);
}
If you’re using an older version of Java, you can do the same thing with a little more work:
private interface Getter<T> {
T get();
}
private interface Validator<T> {
boolean test(T actual, T expected);
}
static <T> T waitForValue(Getter<T> getMethod, Validator<T> validator, T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = getMethod.get();
if (validator.test(actual, expected)) {
return actual;
}
}
return null;
}
private static String waitForString(String expected, int attempts) {
Getter<String> getter = new Getter<String>() {
#Override
public String get() {
return getString();
}
};
Validator<String> validator = new Validator<String>() {
#Override
public boolean test(String actual, String expected) {
return validateString(actual, expected);
}
};
return waitForValue(getter, validator, expected, attempts);
}
private static int waitForInt(int expected, int attempts) {
Getter<Integer> getter = new Getter<Integer>() {
#Override
public Integer get() {
return getInt();
}
};
Validator<Integer> validator = new Validator<Integer>() {
#Override
public boolean test(Integer actual, Integer expected) {
return validateInt(actual, expected);
}
};
return waitForValue(getter, validator, expected, attempts);
}

Avoid using reflection for different reasons: loose of JVM optimizations, your code compiles but explodes at runtime, the code is hard to be debugged.
You can try by creating an interface with the implementations for each type you need to validate.
Something like:
Interface:
public interface InputHandler<T> {
Boolean wait(T expected);
}
Implementations:
An handler implementation for the input String:
public class StringHandler implements InputHandler<String> {
#Override
public Boolean wait(String expected) {
String actual = getString();
return validateString(actual, expected);
}
private String getString() {
// ...
return null;
}
private boolean validateString(String actual, String expected) {
// ...
return false;
}
}
An handler implementation for the input Integer:
public class IntegerHandler implements InputHandler<Integer> {
#Override
public Boolean wait(Integer expected) {
Integer actual = getInt();
return validateInt(actual, expected);
}
private boolean validateInt(Integer actual, Integer expected) {
// ...
return false;
}
private Integer getInt() {
// ...
return null;
}
}
You can add and remove all the "handlers" you need really fast.
App to run the example:
public class Test {
public static void main(String[] args) {
waitForValidInput(new StringHandler(), "a", 3);
waitForValidInput(new IntegerHandler(), 5, 3);
}
static private <T> T waitForValidInput(InputHandler<T> validator, T expected, int attempts) {
for (int i = 0; i < attempts; i++) {
if(validator.wait(expected)) {
return expected;
}
}
return null;
}
}

Try this:
static <T> T helperMethod(Method method, Method validator, T expected, int attempts) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
T actual = null;
for (int i = 0; i < attempts; i++) {
actual = (T)method.invoke(null);
if ((Boolean)validator.invoke(null, actual, expected)) {
return actual;
}
}
return null;
}
(also added exceptions in the signature and changed getMehod to method in the arguments)

Related

How to count a type "object" selected at runtime

I need to count a specific Object, but I'll know which object only at runtime.
Right now I have something like
public class Details {
private String typeOfObjectRequired;
private int numberOfObjectRequired;
}
And in another class I have
public class Container {
private List<Type1> type1List;
private List<Type2> type2List;
private Type3 type3Object;
public int countType1() {
return type1List.size();
}
public int countType2() {
return type2List.size();
}
public int countType3() {
return type3Object.getNumberOfSomething();
}
}
Now I'm doing like this (in a third class that has both Details and Container as attributes)
public boolean hasNumberOfObjectRequired() {
int count = 0;
String type = details.getTypeOfObjectRequired();
if(type.equals("type1")) count = container.countType1();
else if (type.equals("type2")) count = container.countType2();
else if (type.equals("type3")) count = container.countType3();
if (count > details.getNumberOfObJectRequired) return true;
return false;
}
Is there a better way to do this? I don't like to have so many if, also because I have more than just 3 different types.
EDIT:
Right now I have 5 different types, and I always need only one of them.
Basically I want to call different methods based on the String
The Container class could contain a Map of the lists it's composed of:
class Container {
private Map<String, List<?>> lists = new HashMap<>();
private List<TypeOne> first = ...;
private List<TypeTwo> second = ...;
public Container() {
lists.put("type1", first);
lists.put("type2", second);
}
public int count(String type) {
return lists.get(type).size();
}
}
You can grab the size based on the type by calling count:
public boolean hasNumberOfObjectRequired() {
String type = details.getTypeOfObjectRequired();
int requiredCount = details.getNumberOfObjectRequired();
return container.count(type) >= requiredCount;
}
You can use reflection...
public boolean hasNumberOfObjectRequired() {
int count = 0;
String type = details.getTypeOfObjectRequired();
Method m = Container.class.getMethod("countType"+type.charAt(4));
return m.invoke(container) > details.getNumberOfObJectRequired);
}
Or you can use a switch
switch(type){
case "type1":
count = ...
break;
case "type2"
....
}
Even better if type is a int instead of a string

Polymorphic Misunderstand

When i call s1.dub(7) or s2.dub(7) it doesn't work
,but calling it with a string like s2.dub("9") works and prints the doubled string
Could any one tell me why?
Here's the code
interface Inter {
int number();
}
abstract class Abs {
static int foo = 12;
int number() { return 5; }
abstract int ace();
}
final class Sub extends Super {
Sub(int bar) { foo = bar; }
public int number() { return 10; }
int ace() { return 13; }
int dub(int i) { return 2 * i; }
}
public class Super extends Abs implements Inter {
public int number() { return 11; }
public static void main(String args[]) {
Super s1 = new Super();
Super s2 = new Sub(16);
//System.out.println(s1.dub(7)); //doesn't work
//System.out.println(s2.dub(7)); //doesn't work
//System.out.println(s1.dub("7")); //works giving 77
//System.out.println(s2.dub("7")); //works giving 77
}
int twice(int x) { return 2 * x; }
public int thrice(int x) { return 3 * x; }
int ace() { return 1; }
String dub(String s) { return s + s; }
}
Very easy.. you class Super defines a method:
String dub(String s) { return s + s; }
in your main method you instantiate Super:
Super s1 = new Super(); // this has a dub( String ) method
then you try to call this method (dub) passing a integer, instead of a string:
System.out.println(s1.dub(7)); // s1.dub(...) takes a String, not a number
EDIT: This code should not compile, or run, because you are assigning both instances to the super class Super (which does not define a dub(int) method).
Not sure how you are getting exceptions?
Thank you #Jean-FrançoisSavard - I totally missed that!
EDIT2: The original question was modified and no longer indicates that an exception is thrown, which makes sense as the code should not compile at all.
EDIT3: (last one, due to original question changing)
System.out.println(s1.dub(7)); //- this will never work unless you change your class' definition
System.out.println(s2.dub(7)); //- will work if you also change the following line:
from:
Super s2 = new Sub(16);
to:
Sub s2 = new Sub(16);

JAVA: Parsing String to static final int value

I have simple problem, but I'm not able to fix it. I have this interface...
public interface KeyInput extends Input {
public static final int TEST1 = 0x01;
public static final int TEST2 = 0x02;
}
...this string variable...
String inputString = "TEST1";
...and this method.
public void doSomething(int _input) {
}
I want to parse inputString variable to KeyInput static final int value. So that I could call....
doSomething(KeyInput.parse(inputString));
I know the enum valueOf, but this doesn't work here...
If you have only these two (or any other fixed number of) values, you might just enumerate them in switch:
public static int parse(String input) {
int res = -1;
switch (input) {
"TEST1":
res = TEST1;
break;
"TEST2":
res = TEST2;
break;
// ... other options
default: throw new IllegalArgumentException("unknown string");
}
}
The other option is to keep this values inside some map, so you can do this:
private static final Map <String, Integer> TESTS = new HashMap<>();
static {
TESTS.put("TEST1", 0x01);
TESTS.put("TEST2", 0x02);
// ...
}
public static int parse(String input) {
if (TESTS.containsKey(input))
return TESTS.get(input);
else
throw new IllegalArgumentException("unknown string");
}
Still, if you see the enums as an option in your case, I can consider this solution:
public enum Keys {
TEST1(0x01), TEST2(0x02);
int value;
private Keys(int value) {
this.value = value;
}
public getValue() {
return value;
}
}
Here you'll just do valueOf as you suggesed:
public static int parse(String input) {
return Keys.valueOf(input).getValue();
}
If all these options is now for your case, you should use reflection (though, I'm quite sure, it's not the case):
public static int parse(String input) {
Field[] fields = KeyInput.class.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(fields.getModifiers()) && field.getDeclaringClass().equals(int.class) && field.getName().equals(input)) {
return field.getInt(null);
}
}
throw new IllegalArgumentException("unknown string");
}

java generics T extends Simpletype?

I'd like to write a method, that does return something of a PrimitiveType like float, integer, boolean and also String if possible. I'd like to use generics for it but i stuck and dont find a solution for it. I do need it for a Configparser. Ill use it to get different values from the Config.
Current it des look like this and i know that the switch does not work like this but you get an idea of what id like to do:
public class ConfigurationManager extends XmlReader {
private final static String FILE_PATH = "config/config.cfg";
private static Element xml;
public ConfigurationManager() throws IOException {
FileHandle handle = Gdx.files.internal(FILE_PATH);
this.xml = this.parse(handle);
}
public Resolution getResolution() {
Resolution r = new Resolution();
r.height = xml.getFloat("height");
r.width = xml.getFloat("width");
return r;
}
public static <T> T getConfig(Class<T> type, String name) {
if (type.equals(Integer.class)) {
return type.cast(xml.getInt(name));
} else if (type.equals(Float.class)) {
return type.cast(xml.getFloat(name));
} else if (type.equals(Boolean.class)) {
return type.cast(xml.getBoolean(name));
} else if (type.equals(String.class)) {
return type.cast(xml.get(name));
}
throw new AssertionError("Invalid type");
}
}
Thanks alot
Well, I don't think you can do it with primitive types directly, but how about something like this:
public static <T> T getConfig(Class<T> type, String name) {
if(type.equals(Integer.class)){
return type.cast(xml.getInteger(name));
} else if(type.equals(Float.class)){
return type.cast(xml.getFloat(name));
} else if(type.equals(Double.class)) {
return type.cast(xml.getDouble(name));
} else if(type.equals(String.class)) {
return type.cast(xml.getString(name));
}
throw new AssertionError("Invalid type");
}
You could use an Enum to avoid the branching logic and the explicit casting.
public enum TypeSelector {
INTEGER() {
#Override
public Integer getValue(Elements xml, String name) {
return xml.getInteger(name);
}
},
DOUBLE() {
#Override
public Double getValue(Elements xml, String name) {
return xml.getDouble(name);
}
};
private static final Map<Class<?>, TypeSelector> SELECTORS = new HashMap<Class<?>, TypeSelector>() {
{
put(Integer.class, INTEGER);
put(Double.class, DOUBLE);
}
};
public static <T> TypeSelector getSelectorForType(Class<T> c) {
TypeSelector selector = SELECTORS.get(c);
if (selector == null) {
throw new AssertionError("Invalid type");
}
return selector;
}
public abstract <T> T getValue(Elements xml, String name);
}

Replace String Literals If/elseIf block with Enum

I'm new to using Java Enums and I've read that replace IF logic that compares String literals should be replaced with an Enum. I don't quite understand how to replace my below code with an Enum, any ideas? Based on the col value being passed into applyEQ, I need to do a base the next method call on it's value. I do know the possible values of col ahead of time and I'm using a constants file for now. Should I create an Enum and place it in my Interface of Constants file?
public class FilterHelper implements IFilterHelper {
private final EQuery eQuery;
public FilterHelper(EQuery query) {
eQuery = query;
}
#Override
public void applyEQ(String col, String val) throws Exception {
int return = 0;
if (col.equalsIgnoreCase(EConstants.NAME)) {
ret = Sample.addName(eQuery, val);
} else if (col.equalsIgnoreCase(EConstants.KEYWORDS)) {
ret = Sample.addKey(eQuery, val);
} else if (col.equalsIgnoreCase(EConstants.ROLE)) {
ret = Sample.addRole(eQuery, val);
}
if (return != 0) {
throw new Exception("failed");
}
}
}
EConstants.java
public final class EConstants {
public static final String NAME = "cewName";
public static final String KEYWORDS = "cewKeywords";
public static final String ROLE = "cewRole";
}
First create an enum:
public enum EConstants {
CEWNAME,
CEWROLE,
CEWKEYWORDS;
}
Then convert col String to this enum and use switch:
public void applyEQ(String col, String val) throws Exception {
int ret = 0;
final EConstants constant = EConstants.valueOf(col.toUpperCase());
switch(constant) {
case CEWNAME:
ret = Sample.addName(eQuery, val);
break;
case CEWROLE:
ret = Sample.addRole(eQuery, val);
break;
case CEWKEYWORDS:
ret = Sample.addKey(eQuery, val);
break;
default:
throw new Exception("Unhandled enum constant: " + constant);
}
}
Note that EConstants.valueOf() can throw IllegalArgumentException if col.toUpperCase() does not match any of constant values.
BTW I hate local variables initialized in multiple places (and break keyword), try extracting method:
final EConstants constant = EConstants.valueOf(col.toUpperCase());
final int ret = processSample(val, constant);
And the method itself:
private int processSample(String val, EConstants constant) throws Exception {
switch(constant) {
case CEWNAME:
return Sample.addName(eQuery, val);
case CEWROLE:
return Sample.addRole(eQuery, val);
case CEWKEYWORDS:
return Sample.addKey(eQuery, val);
default:
throw new Exception("Unhandled enum constant: " + constant);
}
}
You can rewrite your EConstants as enum:
public enum EConstants {
NAME, KEYWORDS, ROLE
}
And evaluate condition using switch statement:
// col has type of EConstants
switch (col) {
case NAME:
// do something
break;
case KEYWORDS:
// do something
break;
case ROLE:
// do something
break;
default:
// what to do otherwise
break;
}
The great thing about Java Enums is that they provide language level support for the type safe enum pattern, because among other things it allows you to define methods and even override them. So you could do this:
public enum CewColumn {
NAME("cewName") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addName(eQuery, val);
}
},
KEYWORDS("cewKeywords") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addKey(eQuery, val);
}
},
ROLE("cewRole") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addRole(eQuery, val);
}
};
private final String colName;
private MyColumn(String colName) {
this.colName = colName;
}
private static final Map<String, CewColumn> COLUMNS = new HashMap<>(values().length);
static{
for (CewColumn cewColumn : values()){
COLUMNS.put(cewColumn.colName, cewColumn);
}
}
public abstract int add(EQuery eQuery, String val);
public static CewColumn getCewColumn(String colName){
return COLUMNS.get(colName);
}
}
Then you can use it like this:
CewColumn cewColumn = CewColumn.getCewColumn(colName);
if (cewColumn != null){
int ret = cewColumn.add(eQuery, val);
}
-> You replaced the switch statement with polymorphism!
it is best to create a Enum.
public Enum AvailableCols{
COL_1,
COL_2;
}
and convert the procedure as
public void applyEQ(AvailableCols col, String val) throws Exception {
switch(col){
case COL1:
...
If you still want the string to be preserved you can see the following post
Basically create an enum and change the type of col and use equals() or == to compare the value of col against the enum values. Alternatively you could use a switch statement but I doubt that would make your code more readable for only 3 constants.
Example:
enum EConstants {
NAME,
KEYWORDS,
ROLE;
}
public void applyEQ(EConstants col, String val) throws Exception {
if( col == EConstants.NAME ) {
...
}
....
}
//or
public void applyEQ(EConstants col, String val) throws Exception {
if( EConstants.NAME.equals(col) ) { //col might be null
...
}
....
}
//or
public void applyEQ(EConstants col, String val) throws Exception {
switch( col ) {
case NAME:
...
break;
case ROLE:
...
}
}
http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
If your raw data is a string, you will still need to do a string comparison to assign the enum. This might be faster if you do a lot of comparisons on the result data, but if not, it simply adds complication to your code.
You can iterate over the values of the enum like a collection, which gives you an advantage when you need to add constants. That's not bad.
Here is how to do it:
public enum EConstants {
NAME, KEYWORDS, ROLE
}
...
public EConstants setConstant(String from) {
if (from.equalsIgnoreCase("cewName")) {
return NAME;
} else if (col.equalsIgnoreCase("cewKeywords")) {
return KEYWORDS;
} else if (col.equalsIgnoreCase("cewRole")) {
return ROLE;
}
}
You preprocess your data that way and now when you are trying to figure out logic you can use a switch on the enum type value.
Here is a trick for you. No switch/case (just come up with a better name for EConstants).
public enum EConstants {
NAME,
KEYWORDS,
ROLE;
private interface Applier {
void apply(EQuery query, String val);
}
public void apply(EQuery query, String val) {
map.get(this).apply(query, val);
}
private static Map<EConstants, Applier> map = new HashMap<EConstants, EConstants.Applier>();
static {
map.put(NAME, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addName(query, val);
}
});
map.put(KEYWORDS, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addKey(query, val);
}
});
map.put(ROLE, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addRole(query, val);
}
});
}
}
Now you just write:
#Override
public void applyEQ(EConstants econs, String val) {
econs.apply(equery, val);
}

Categories

Resources