I have a POJO looking like this:
public class Pojo implements Comparable<Pojo> {
private String type;
private String journalId;
private Date bookingDate;
private Long account;
private String description;
private BigDecimal debit;
private BigDecimal credit;
....
}
and I want to sort a list of these POJOs. Currently my compareTo method looks like this:
#Override
public int compareTo(EfdisJournal other) {
int i = this.type.compareTo(other.type);
if (i != 0)
return i;
if (this.bookingDate != null && other.bookingDate != null)
i = this.bookingDate.compareTo(other.bookingDate);
if (i != 0)
return i;
if (this.journalId != null && other.journalId != null)
i = this.journalId.compareTo(other.journalId);
if (i != 0)
return i;
return this.account.compareTo(other.account);
}
If I run a sort with this compareTo method, I get this java.lang.IllegalArgumentException: Comparison method violates its general contract error. I did google a bit and I think it happens because some of the fields are null on comparison. Yet I have no idea how to solve this or if I am right why that error appears.
The comparison should work like this: 1st compare by type, then compare by bookingDate, as 3rd compare by journalId and at last compare by account. All comparisons should be ascending.
type is never null
bookingDate may be null
journalId may be null
account is never null
EDIT:
Sadly I was not able to implement the method, so that the order is as needed. Yet, i solved the problem I had, because the stored procedure yielded 2 resultsets, of which the second was order as needed, so the only thing I had to do was to use the 2nd resultset instead of the first.
You need to deal with the case where one instance has a null bookingDate, and the other has a non-null bookingDate.
You should decide whether things with null bookingDate should be sorted before or after things with a non-null bookingDate, and write your compareTo appropriately. (And then journalId too.) Then you can get an order that sorts consistently.
For instance:
#Override
public int compareTo(EfdisJournal other) {
int i = this.type.compareTo(other.type);
if (i != 0) {
return i;
}
if ((this.bookingDate==null) ^ (other.bookingDate==null)) {
return (this.bookingDate==null ? -1 : 1);
}
if (this.bookingDate != null && other.bookingDate != null) {
i = this.bookingDate.compareTo(other.bookingDate);
}
if (i != 0) {
return i;
}
if ((this.journalId==null) ^ (other.journalId==null)) {
return (this.journalId==null ? -1 : 1);
}
if (this.journalId != null && other.journalId != null) {
i = this.journalId.compareTo(other.journalId);
}
if (i != 0) {
return i;
}
return this.account.compareTo(other.account);
}
You're ignoring situations where bookingDate and/or journalId is null with one and non-null with the other.
Related
I am new to Java. I am facing an issue now in which I couldn't find the easiest and cleanest way of solving it.
Suppose I have 3 parameters(string) passed to a function(could be a Hashmap too).I want to check if individual variable or combination of variables is not Null and act accordingly.
For example one way to do this is using if-else this way
if(a!=null && b == null && c == null) {
//doSomething
}
else if(a==null && b!= null && c == null ) {
//doSomething
}
else if(a==null && b0= null && c != null) {
//doSomething
}
......
//Similarly combination of two variables
if(a!=null && b != null && c == null) {
//doSomething
}
else if(a!=null && b== null && c != null) {
//doSomething
}
else if(a==null && b!= null && c != null) {
//doSomething
}
......
//and so on
//Similarly combination of three variables
if(a!=null && b != null && c != null) {
//doSomething
}
....
How to achieve this kind of situation. I found similar question, but didn't make the code clean. Any help will be appreciated
Write these utility functions and you can compare n terms easily.
public static boolean areAllNull(Object... objects) {
return Stream.of(objects).allMatch(Objects::isNull);
}
public static boolean areAllNotNull(Object... objects) {
return Stream.of(objects).allMatch(Objects::nonNull);
}
you can use these functions for n comparisons.
if(areAllNotNull(a) && areAllNull(b,c)) {
//doSomething
}
else if(areAllNotNull(b) && areAllNull(a,c)) {
//doSomething
}
else if(areAllNotNull(c) && areAllNull(b,a)) {
//doSomething
}
This is my solution. Note, that you have multiple if...else in one single method. And then you add doSomething. This is going to be terrible to ready and later to realize.
What about to move one single condition into separate method and name it with relative name. Then, lets encapsulate it into Consumer and all of it into a predefined list. Later, if your doSomething will be huge, then you can move from single method to single class, not modifying client code.
This is class, to collect required variable for conditions:
final class Data {
private final String a;
private final String b;
private final String c;
}
Then define one Consumer per on if statement:
Consumer<Data> wonderfulConsumer = data -> {
if (a != null && b == null && c == null) {
// do something for wonderful consumer
}
};
Consumer<Data> badLuckConsumer = data -> {
if (a == null && b != null && c == null) {
// do something for bad luck consumer
}
};
Note, all these consumers could be modified separately (even be in the different classes).
Then in the client code, define list of all known consumers: List<Consumer<Data>> consumers = Arrays.asList(wonderfulConsumer, badLuckConsumer).
And finally your method will be like this and you do not need to change it when you decide to modify or add consumers.
Data data = new Data(a, b, c);
consumers.forEach(consumer -> consumer.accept(data));
If I had to do this , i will do it in the same way that you have done.
but if you dont like that and if you think it is not readable you can do it in this way, i expect lot of negative comments to this answer because this is a bad solution.
public static void yourMethod(Object a,Object b,Object c)
{
int evaluation = howManyNotNull(a,b,c);
if(evaluation == 0) // all are nulls
{
// your code
}
else if(evaluation == 1) // only one is not null
{
if(a!=null)
{
}
else if(b!=null)
{
}
else
{
// c not null
}
}
else if(evaluation == 2) // two variables are not null but other one is null
{
if(a==null)
{
}
else if(b == null)
{
}
else
{
// c is null, a and b not null
}
}
else
{
// all abc not null
}
}
private static int howManyNotNull(Object a, Object b, Object c)
{
return (a==null?0:1) + (b==null?0:1) + (c==null?0:1);
}
There is extended version of this , assign 3 prime values for a, b , c (example :a=2,b=3,c=5), then use a supportive method like this
private static int convertAvailabilityToInt(Object a, Object b, Object c)
{
return (a==null?1:2) * (b==null?1:3) * (c==null?1:5);
}
if the answer is 1 ,then all are not null .
You can use for example a 3 digit string simulating 3 flags.
You first set it to "000".
Then you check each variable for null, if it is not you will replace the 0 with 1.
Then you could use switch cases to treat each case.
You are doing everything right but you have to remember that primitive data types cannot be null. For example string is not null, but empty string "", or int cannot be null, its by default sets to 0. In conclusion Objects like Map , ArrayList or Integer.. you can check for null, but primitive data types cannot be null, so you cannot check them for it. For deeper understanding just learn about primitive and advanced data types.
I hope I got your problem right :)
I have the below utility method and I am using multiple if statements and getting cognitive complexity issue. I went through some links, but I am not able to understand how should I change my code without affecting users of this method.
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
String key=null;
boolean isValidWrapper = false;
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper= wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
if(wrapper != null && wrapper.equalsIgnoreCase("TFR")) {
isValidWrapper=Boolean.TRUE;
}
try {
key = wrapper.getKey();
}
catch (Exception exception) {
return isValidWrapper;
}
if(key!=null) {
Date tokenExpiryTime = key.getExpiresAt();
if(tokenExpiryTime!=null) {
return isValidWrapper;
}
String algorithm=key.getAlgorithm();
if(!DESIRED_ALGO.equals(algorithm)) {
return isValidWrapper;
}
String value6=key.getType();
if(!DESIRED_TYPE.equals(value6)) {
return isValidWrapper;
}
if(key.getValue1()!=null && key.getValue2().size()>0 && key.getValue3()!=null && key.getValue4()!=null && key.getValue5()!=null) {
isValidWrapper=Boolean.TRUE;
}
}
return isValidWrapper;
}
Please share your suggestions to refactor this code.
I don't think that merging many if conditions to one or simply do a code clean up, for example by changing the order of some instructions, can solve your problem.
Your code does not match the single responsibility principle. You should refactor this big method to smaller parts. Due to this it will testable, easier to maintain and read. I spent some time and did this:
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken) {
final WrapperClass unpackedWrapper = unpackWrapper(wrapper);
boolean wrapperValid = isUnpackedWrapperValid(unpackedWrapper);
Key key = null;
try {
key = unpackedWrapper.getKey();
} catch (final Exception exception) {
return wrapperValid;
}
if (key != null) {
if (doesKeyMeetsBasicConditions(key)) {
return wrapperValid;
}
if (doesKeyMeetsValueConditions(key)) {
return true;
}
}
return wrapperValid;
}
protected static WrapperClass unpackWrapper(final WrapperClass wrapper) {
if (wrapper != null && wrapper.length() > 7 && wrapper.substring(0, 6).equalsIgnoreCase("XYZ")) {
return wrapper.substring(7, wrapper.lastIndexOf('.') + 1);
}
return wrapper;
}
protected static boolean isUnpackedWrapperValid(final WrapperClass wrapper) {
return wrapper != null && wrapper.equalsIgnoreCase("TFR");
}
protected static boolean doesKeyMeetsBasicConditions(final Key key) {
Date tokenExpiryTime = key.getExpiresAt();
if (tokenExpiryTime != null) {
return true;
}
String algorithm = key.getAlgorithm();
if (!DESIRED_ALGO.equals(algorithm)) {
return true;
}
String value6 = key.getType();
return !DESIRED_TYPE.equals(value6);
}
protected static boolean doesKeyMeetsValueConditions(final Key key) {
return key.getValue1() != null && key.getValue2().size() > 0
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null;
}
I don't know the domain logic, so some of my methods have stupid names etc. As you can see, now you have a lot of smaller methods with not many branches (if conditions) - easier to test (a static code is not nice, but you can mock it by using for example PowerMock).
A bit of rewriting delivered a simplification, that still could be improved upon.
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper = wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
boolean isValidWrapper = wrapper != null && wrapper.equalsIgnoreCase("TFR");
try {
String key = wrapper.getKey();
if (key != null && key.getExpiresAt() == null
&& DESIRED_ALGO.equals(key.getAlgorithm())
&& DESIRED_TYPE.equals(key.getType())
&& key.getValue1() != null && !key.getValue2().isEmpty()
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null) {
isValidWrapper = true;
}
}
catch (Exception exception) {
// DO NOTHING
}
return isValidWrapper;
}
After comment: here I catch any exception for all calls.
First of all, Sonar should give you more flags: reusing the wrapper parameter is usually a bad practice, NPE where invoking wrapper.getKey because wrapper can be null, but anyway, not the point...
Try reducing the number of if statements by creating local boolean variables (or possibly 1 big if statement if you have less than 5 or 6 tests, but often less readable). Once it's done, you should only have 1 block testing these boolean variables, and have one return statement, like the example above (not necessarily accurate!):
boolean expired = tokenExpiryTime != null;
boolean desiredAlgo = DESIRED_ALGO.equals(key.getAlgorithm());
boolean desiredType = DESIRED_TYPE.equals(value6);
if (expired || !desiredAlgo || !desiredType) {
return isValidWrapper;
}
However, your Cognitive complexity level seems pretty low if this kind of algorithm triggers it...
Another big way to reduce an algorithm complexity is to turn sub-blocks of code (loops, if and try-catch) into private methods. In your example, it could be something like a checkWrapperValidity method, responsible for every test returning isValidWrapper
I have a little problem with this snippet of code:
#Override
public int compareTo(EventResponse o) {
int compare1 = startTime.compareTo(o.startTime);
if (compare1 == 0 && o.myProviderId != null && o.providerId != null) {
return o.providerId.compareTo(o.myProviderId) != 0 ? -1 : 0;
} else {
return compare1;
}
}
I have EventResponse class which i have created that implements Comparable interface, o.myProviderId is ID of currently signed in user (into application) and o.providerId is ID of user that is assigned to particular object (EventResponse).
What I need to achieve is if there are two objects with the same startTime to show objects assigned to me first and then objects assigned to some other provider/s.
This code snippet should (and I think it did in the past) do the trick but I get error message stating: "Comparison method violates its general contract".
Please help!
The violation of the contract often means that the comparator is not providing the correct or consistent value when comparing objects.
For example :
if (compare1 == 0 && o.myProviderId != null && o.providerId != null) {
return o.providerId.compareTo(o.myProviderId) != 0 ? -1 : 0;
}
So, what happens when o.providerId.compareTo(o.myProviderId) gives you 1 or -1. In both scenario you are going to return -1 isn't it ?
Instead, why not simply return what gets return from compareTo ?
if (compare1 == 0 && o.myProviderId != null && o.providerId != null) {
return o.providerId.compareTo(o.myProviderId);
}
I have a class as below, before I set the data I need to check whether getValue() is present and it's value is empty.
public class Money {
{
private String value;
private String currency;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getCurrency() {
return currency;
public void setCurrency(String currency) {
this.currency = currency;
}
}
//JSON is like this
"money": {
"currency": "USD",
"value": ""
}
I want to check whether this getValue() is present or not like obj.getMoney().getValue() != null,
and then I need to check it's value is empty... obj.getMoney().getValue().equals("") but it fails on this condition obj.getMoney().getValue() != null as null.
If the following check fails
if (obj.getMoney().getValue() != null) { ... }
then it implies that the money object itself is null. In this case, you can slightly modify your if condition to check for this:
if (obj.getMoney() != null && obj.getMoney().getValue() != null) { ... }
You said that first you need to check whether value is null or not and then also check whether the value is empty or not,
You can do the following
if (obj.getMoney() != null && obj.getMoney().getValue() != null && !obj.getMoney().getValue().isEmpty()) {
// rest of the code here
}
obj.getMoney().getValue() will give you null pointer exception. You should check for null object before using . after it. Example code:
Below code looks huge but it's actually readable and it will be optimized by compiler.
if(obj != null){
Money money = obj.getMoney();
if(money != null) {
String value = money.getValue();
//Add you logic here...
}
}
I think you are getting null point exception. You are facing this exception because obj.getMoney() is already null. Since you are trying to get a null object's value, so you are getting this exception. Correct code will be
if ((obj.getMoney() != null) && (obj.getMoney().getValue().trim().length() > 0)) {
// Execute your code here
}
When instantiating your obj, gives a new. The form of validation is correct, the problem is in the obj that was not initialized. (I believe)
I have a class "entry" which has several date fields:
creationDate (always available)
manualSetDate (not always)
meetingDate (not always)
Now I'd like to sort the entries by first meetingDate (to have all entries of a meeting together), then or if not available at one entry by manualSetDate, and if they have the same meeting or if one entry has no manualSetDate also by the creationDate.
The difficulty is that many entries don't have a meeting- or a manualSetDate. So I have iterated over all (I hope so) possible combinations like:
Date relevant1; //at the end i compare both relevant dates
Date relevant2;
if e1.creationdate != null && e2.creationdate == null)
{relevant2 = e2.creationdate}...
if e1.creationdate == null && e2.creationdate != null)
{relevant2 = e2.creationdate}...
...
Which leads to high complexity
Sort order:
Meeting Date (if both have one)
ManualSetDate (if both have one)
CreationDate
I traversed over all combinations, but it seems to make mistakes...
Is there a better way to do the sorting? any ideas?
I think I would make a function in my Entry that found the "relevant" date. Then have your compareTo use that new function. This way you aren't accounting for every possible combination as your code above does.
public class Entry implements Comparable<Entry>
{
private Date getAvailableDate()
{
if (meetingDate != null)
return meetingDate;
else if (manualSetDate != null)
return manualSetDate;
else
return creationDate;
}
public int compareTo(Entry other)
{
return this.getAvailableDate().compareTo(other.getAvailableDate());
}
}
Better to write your own Comparator and implement compare method something like below code
class MyComparator implements Comparator<DateEntry>{
#Override
public int compare(DateEntry o1, DateEntry o2) {
int i = o1.creationDate.compareTo(o2.creationDate);
if (i != 0) return i;
if(o1.manualSetDate!=null&&o2.manualSetDate!=null)
i = o1.manualSetDate.compareTo(o2.manualSetDate);
if (i != 0) return i;
if(o1.meetingDate!=null&&o2.meetingDate!=null)
i = o1.meetingDate.compareTo(o2.meetingDate);
if (i != 0) return i;
return i;
}
}
And then Sort them like.
Collections.sort(list,new MyComparator ());
I suggest that you use Comparator to define order and then some library function to do actual sorting. I would also go for multiple early returns in compare method. Implementation is something like following:
public class Entry {
...
public Date getMeetingDate() { return meetingDate;}
public Date getManualSetDate() { return manualSetDate; }
public Date getCreationDate() { return creationDate; }
}
public class EntryComparator implements Comparator<Entry> {
#Override
public int compare(Entry o1, Entry o2) {
if (o1.getMeetingDate() != null && o2.getMeetingDate() != null) {
int compared = o1.getMeetingDate().compareTo(o2.getMeetingDate());
if (compared != 0) {
return compared;
}
}
if (o1.getManualSetDate() != null && o2.getManualSetDate() != null) {
int compared = o1.getManualSetDate().compareTo(o2.getManualSetDate());
if (compared != 0) {
return compared;
}
}
return o1.getCreationDate().compareTo(o2.getCreationDate());
}
}
//and actual sorting:
List<Entry> entries ...
Collections.sort( entries, new EntryComparator() );