I'm trying to create a counter that will rollover whenever it reaches a preset ceiling and resets back to its floor value upon reaching said ceiling. I have implemented the class and it works just fine. However, on my way to my solution, I wanted to experiment with Java Generics. I want to try and extend my counter so that it doesn't only use integers, but instead can use any type of number. I know that counters typically call for just the use of integers anyway, but I wanted to see if it could be done.
I figured that the code would be similar to below. However, java.lang.Number doesn't have a "generic" way of getting/setting its value. Do I need to create my own number class to enable this? Also, I know that if I do get this working, I need to alter my equals checks so that they have an error threshold for floating point values, this is more or less a modified version of my int counter with what I figured would work for generics.
Edit:
It's been suggested that I take a mapping approach where I store an integer counter and keep a increment value so that when I want to spit out a number, I just multiply my current count by the increment value. However, I don't believe this will fill my exact needs because I don't want to necessarily increment by the same amount every time. The main focus of this counter is more of a way to have a fixed range number that, when added to or subtracted from, knows how to handle wrapping back around.
I guess the best way to describe it (although probably improperly) would be like an Integer that automatically handles over/underflow.
package com.math;
public class GenericRolloverCounter<T extends Number> {
private T value;
private T lowValue;
private T highValue;
public GenericRolloverCounter(T l_startValue, T l_highValue) {
this.lowValue = l_startValue;
this.highValue = l_highValue;
this.value = l_startValue;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public void increment(T valToIncrementBy) {
this.value += valToIncrementBy;
if (this.value > this.highValue) {
this.value = (this.lowValue + (this.value - (this.highValue + 1)));
}
}
public void increment() {
this.increment(1);
}
public void decrement(T valToDecrementBy) {
this.value -= valToDecrementBy;
if (this.value < this.lowValue) {
this.value = ((this.value + this.highValue + 1) - this.lowValue);
}
}
public void decrement() {
this.decrement(1);
}
#Override
public String toString() {
return Integer.toString(this.value);
}
}
You might want to also specify an amount by which to count. Default value would be 1.
You can get around some of this by using the Number method .doubleValue() and doing double arithmetic.
Here is one of the methods converted to use this idea.
public void decrement(double valToDecrementBy) {
double work = this.value.doubleValue();
work -= valToDecrementBy;
// should use some value related to incrementing amount
if ((this.value.doubleValue() - this.lowValue.doubleValue()) < 0.1D) {
work = ((this.value.doubleValue() + this.highValue.doubleValue() + 1) - this.lowValue.doubleValue());
}
// ... no way to put it back
}
But, there is still no way to put the value back that's clean and easy. Since 'Number' only has a few commonly used non-abstract subclasses, you could do some ugly instanceof stuff to store the value back. It would look something like this:
if (theValue instanceof Double) { // depends on it having a non-null value prior
theValue = (T)(new Double(work));
}
Or you could convert the starting values to double when you start and just work with doubles.
private double value;
private double lowValue;
private double highValue;
public GenericRolloverCounter(T l_startValue, T l_highValue) {
this.lowValue = l_startValue.doubleValue();
this.highValue = l_highValue.doubleValue();
this.value = l_startValue.doubleValue();
}
That does introduce the issues of incrementing floating point values and the rounding/evaluation problem there.
Oh ... and your toString() should be:
return value.toString();
To use the native toString() method on the T class.
#Crusher's comments suggest another way to do it. Map everything to 'int' and keep a multiplier. Here's some bits of code to show what I mean. (Thanks Crusher)
private int value;
private int lowValue;
private int highValue;
private double incr;
public GenericRolloverCounter(T l_startValue, T l_highValue, T incrementAmount) {
double incr = incrementAmount.doubleValue();
this.lowValue = Math.round(l_startValue.doubleValue() / incr);
this.highValue = Math.round(l_highValue.doubleValue() / incr);
this.value = Math.round(l_startValue.doubleValue() / incr);
}
public void increment(int valToIncrementBy) {
this.value += valToIncrementBy;
if (this.value > this.highValue) {
this.value = (this.lowValue + (this.value - (this.highValue + 1)));
}
}
#Override
public String toString() {
return String.valueOf(incr * this.value);
}
Related
Is there a way in Java to create a collection (map) with fixed size and length?
I.e., I would like to initialize it with K constant keys (e.g. strings) but still want to be able to change the values.
Edit:
The test case has a fixed number of objects, each one corresponds to a number (float). Each time a specific event in the application occurs, I would like to multiply all the numbers in the collection, except the number that corresponds to the object that "caused" the event.
The number is not logically an attribiute of the object.
I suggest you first look at Mike's answer to get an idea of how to go about solving this problem, then make some changes to the code he provided so it will work in your situation:
import java.util.HashMap;
public class InstrumentedHashMap<K> extends HashMap<K, Float> {
private static final long serialVersionUID = 1L;
private int MAX;
public InstrumentedHashMap(int capacity) {
super();
MAX = capacity;
}
#Override
public Float put(K key, Float value) {
if (super.size() >= MAX && !super.containsKey(key)) {
return null;
} else {
super.put(key, value);
return value;
}
}
public void event(K trigger, int multiplyAmount, float subtractAmount) {
super.entrySet().stream().forEach(e -> {
if (!e.getKey().equals(trigger))
e.setValue(e.getValue() * multiplyAmount);
else
e.setValue(e.getValue() - subtractAmount);
});
}
}
You can use the InstrumentedHashMap#event method to handle your "specific event", with the multiplyAmount parameter being the value that you want to multiply your floats by.
Ok so in my cs class we have an assignment that requires us to return a value and then set it to zero. I can't figure out how to do this without using a secondary variable(which would break requirements) so I would appreciate some help. here are the exact requirements.
"Has a use() method that returns the value contained in the points field. It also resets the points field to zero. You’re going to have to think about the order of operations here to make this work correctly."
package Game;
import java.util.Random;
public class HealthPotion
{
private int points;
boolean Haspotion;
HealthPotion()
{
Random num1 = new Random();
int num = num1.nextInt(10)+1;
points=num*10;
}
public int Use()
{
return points;
}
public int getPoints()
{
return points;
}
}
That's not really possible without abusing a finally block, i.e.
try {
return points;
} finally {
points = 0;
}
However it's really hard to believe that would be what's wanted, since it's not a good idea to write code like that.
Include a setter method like this.
public void setValue(){
this.points=0;
}
Call this method after you get the value.
How about this?
public int Use()
{
int tmp = points;
points = 0;
return tmp;
}
It has limitations, especially if points can be changed by a different thread while this method executes. But if you are working in a single-threaded environment this should be ok.
This should work
int points = 5;
public void test(){
System.out.println(use() +" " + points);
}
private int use(){
return points - (points = 0);
}
returning 5 0
I am working with a legacy system that can have JSON that can either look like:
{"gauge": 1.0}
// or
{"gauge": "1.0-2.0"}
which is to say that gauge can be either an integer or a string. I want to be able to serialize and deserialize the value to and from the same type.
At first, I thought I could simply create an adapter for this:
public class Capabilities {
private Range gauge;
}
public class Range {
private int value;
private String range;
private boolean isRange;
public Range(int value) {
this.value = value;
this.isRange = false;
}
public Range(String value) {
this.range = range;
this.isRange = true;
}
public boolean isRange() {
return this.isRange;
}
public int getValue() {
return this.value;
}
public String getRange() {
return this.range;
}
}
Then I have RangeTypeAdapter that creates Range with the int/String value as appropriate.
However when I run gson.fromJson(str, Capabilities.class) I get:
Expected BEGIN_OBJECT but was STRING
I've ascertained that this is because the value of gauge in str is a wrapped in double-quotes and that fact that gauge is supposed to be a Range or anything other than a String means that it expects it to be an object.
Is there any way to expect Gson to expect a STRING for a particular class? Moreover, is there a better way handling a possible variance in primitive type?
I'm not aware if the Gson API gives a way to do this, but an option would be to try one conversion and if it fail, attempted the second one.
What I'm saying is to have a GaugeInt class that has gauge as an int field. And to have another class, GaugeString, that has gauge as a String field. Now we could attemp first conversion with say GaugeInt.class; if this causes an exception, we would try the second conversion with GaugeInt.class.
private String gauge = "";
public boolean isRange(){
return (gauge.indexOf("-")>-1);
}
public int getValue(){
if(isRange()){
return Integer.parseInt(gauge.split("-")[0]);
}else{
return Integer.parseInt(gauge);
}
}
public int getSecondValue(){
if(isRange()){
return Integer.parseInt(gauge.split("-")[1]);
}else{
return -1;
}
}
so by calling isRange() you will findout if its 1.0 or 1.0-2.0,
then based on that you can either
call getValue(),
or getValue() and getSecondValue()
which in the case of range will return second int and if not range will return -1 or pick other number to identify this case
no need for other adapters or so.
hope this helps you.
I have some variables:
float firstFloatSize = 0.0;
float secondFloatSize = 1.0;
float thirdFloatSize = 2.0;
float fourthFloatSize = 3.0;
I would like to change these variables from a method, which would receive the name and the new value of the variable as a parameter. Like this:
public void changeValue("firstFloatSize", 3.0);
If I ran that, I would change the value of the variable "firstFloatSize" to 3.0.
Would this be somehow possible? I know I could create a wrapper class for the floats and implemented such a method in that class, but I'm just curious if I could achieve this in another way.
use setProperty from apache common PropertyUtils
PropertyUtils.setProperty(this,"firstFloatSize", 3.0);
You could do this using reflection, but it would create code that would be difficult to maintain. For example, changing a variable name will break the reflection code: you would not notice that until runtime. You'd also be circumventing encapsulation (perhaps moot given you note that the member data are public).
I'd suggest you do this in the normal way, with set and get methods.
If the main discriminatory point of these fields is the "first", "second", etc., then you could use an array to store the values, und set up a method which changes the values according to the index in the array:
float[] floatSizes = new float[]{ 0, 1, 2, 3 };
public void changeValueAtPosition(int index, float newValue){
floatSizes[index] = newValue;
}
This example of course lacks some common guards on the parameters (like ensuring the index is valid), and could be improved by using a Standard Type (like an enum) instead of a primitive int to switch the value you want to change.
Changing the "firstFloatSize" would then be possible by calling changeValueAtPosition(0, 3);
To reiterate the point of other answers: Do Not Use Reflection if there are other, simpler ways. Reflection-based code is a maintenance nightmare in any project size > 100 LOC.
I recommend to use third party libraries.
But if you want to use Java raw APIs, below is a sample code..
public class ReflectionSample {
private float value1 = 1.0f;
public float getValue1() {
return this.value1;
}
public void changeValue(String fieldName, float value) throws Exception {
Field field = this.getClass().getDeclaredField(fieldName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(this, value);
field.setAccessible(accessible);
}
public static void main(String[] args) throws Exception {
ReflectionSample sample = new ReflectionSample();
sample.changeValue("value1", 3.0f);
System.out.println(sample.getValue1());
}
}
You have basically two options:
Either write a custom dispatch method:
public void changeValue(String fieldName, float newValue)
{
// Java 6 style:
if("firstFloatSize".equals(fieldName)) {
targetClass.firstFloatSize = newValue;
} else if("secondFloatSize".equals(fieldName)) {
targetClass.secondFloatSize = newValue;
} else if // ... and so on
// or, alternatively, Java 7 style:
switch(fieldName)
{
case "firstFloatSize": targetClass.firstFloatSize = newValue; break;
case "secondFloatSize": targetClass.secondFloatSize = newValue; break;
// ... and so on
}
}
Or use reflection:
public void changeValue(String fieldName, float newValue)
{
try
{
Field field = targetObject.getClass().getField(fieldName);
field.setFloat(targetObject, newValue);
}
catch( ... ) { ... }
}
I don't know if your floats are fields in the same class or another class. If it's the same class, you don't need the targetObject, obviously. Otherwise, of course, you need to replace targetObject with a reference to the object you want.
Note that both ways are not very nice as other comments already state. Both are not very maintainable and if, for example, you want to rename one of the floats in the future, then you have to manually check for all occurrences of the string, because your compiler cannot determine correctness in these cases.
Also (though this is kind of obvious), if you have multiple similar floats which you want to access like this, you could also create a single array and use an index (possibly backed by an enum) instead of a string to address them. But that goes too far away from the original question so I won't elaborate here further...
As a good coding practice.
You should not have public variables in your class.
Variables should be private with proper get and set methods. That is one of the basic OOPs principle. Encapsulation.
But back to your code.
First there is a small problem in how you have defined float variables. these should be
float firstFloatSize = 0.0f;
Now there are few ways you can achieve this.
Writing your own reflection based code.
Using conditional code something like below.
Using Apache PropertyUtils. Which uses reflection to set your property.
Still in the end your calling code should be calling a proper set method. Hope it helps.
public void changeValue(String property, float value) {
if(property.equals("firstFloatSize")) {
firstFloatSize = value;
} else if(property.equals("secondFloatSize")) {
secondFloatSize = value;
} else if(property.equals("thirdFloatSize")) {
thirdFloatSize = value;
} else {
fourthFloatSize = value;
}
}
Not sure what exactly you need to set values this way, but this is what i came up without reflection:
public class PropertySetter {
private float valOne;
private float valTwo;
private float valThree;
private float valFour;
public void setValue(FIELDS name, float value) {
switch (name) {
case valOne:
valOne = value;
break;
case valTwo:
valTwo = value;
break;
case valThree:
valThree = value;
break;
case valFour:
valFour = value;
break;
default:
throw new AssertionError(name.name());
}
}
public enum FIELDS {
valOne,
valTwo,
valThree,
valFour;
}
}
however if reflection is allowed, then i would do this:
public class ReflectionSetter {
private float valOne;
private float valTwo;
private float valThree;
private float valFour;
public boolean setValue(String name, float value) {
Field[] fields = this.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getName().equals(name)) {
try {
f.setFloat(this, value);
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(ReflectionSetter.class.getName()).log(Level.SEVERE, null, ex);
}
return true;
}
}
return false;
}
}
First method is a bit difficult to maintain because refactoring of this class would be a nightmare.
Second way is quite nice, because you only just need to know fieldname.
Are there any existing utilities like Apache Commons StringUtils that make it easy to increment an integer, but output it as a zero padded string?
I can certainly write my own utilizing something like String.format("%05d", counter), but I'm wondering if there is a library that has this already available.
I'm envisioning something I can use like this:
// Create int counter with value of 0 padded to 4 digits
PaddedInt counter = new PaddedInt(0,4);
counter.incr();
// Print "0001"
System.out.println(counter);
// Print "0002"
System.out.println(counter.incr());
String text = "The counter is now "+counter.decr();
// Print "The counter is now 0001"
System.out.println(text);
I doubt you'll find anything to do this, because padding and incrementing are two basic operations that are unrelated, and trivial to implement. You could have implemented such a class three times in the time you took to write your question. It all boils down to wrapping an int into an object and implementing toString using String.format.
In case anyone is interested, I threw together this a few minutes after posting my question:
import org.apache.commons.lang.StringUtils;
public class Counter {
private int value;
private int padding;
public Counter() {
this(0, 4);
}
public Counter(int value) {
this(value, 4);
}
public Counter(int value, int padding) {
this.value = value;
this.padding = padding;
}
public Counter incr() {
this.value++;
return this;
}
public Counter decr() {
this.value--;
return this;
}
#Override
public String toString() {
return StringUtils.leftPad(Integer.toString(this.value),
this.padding, "0");
// OR without StringUtils:
// return String.format("%0"+this.padding+"d", this.value);
}
}
The only problem with this is that I must call toString() to get a string out of it, or append it to a string like ""+counter:
#Test
public void testCounter() {
Counter counter = new Counter();
assertThat("0000", is(counter.toString()));
counter.incr();
assertThat("0001",is(""+counter));
assertThat("0002",is(counter.incr().toString()));
assertThat("0001",is(""+counter.decr()));
assertThat("001",is(not(counter.toString())));
}
To be honest, I think you are mixing different concerns. An integer is an integer with all the operations and if you want to output it padded with zeros that is different thing.
You might want to have a look at StringUtils.leftPad as an alternative of String.format.