I tried to implement a TreeSet instead of a ArrayList due to the fact that I need to sort that list. The original code was:
private final List<Report> reports = new ArrayList<Report>();
public void receiveReport(final Report report) {
this.reports.add(report);
}
To my version:
private final TreeSet<Report> reports = new TreeSet<>();
public void receiveReport(final Report report) {
reports.add(report);
}
and it show me just 1 report and this error:
Exception in thread "main" java.lang.ClassCastException: class dsa.speedcamera.Report cannot be cast to class java.lang.Comparable (dsa.speedcamera.Report is in unnamed module of loader 'app';
java.lang.Comparable is in module java.base of loader 'bootstrap')
at java.base/java.util.TreeMap.compare(TreeMap.java:1563)
at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:768)
at java.base/java.util.TreeMap.put(TreeMap.java:777)
at java.base/java.util.TreeMap.put(TreeMap.java:534)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at dsa.speedcamera.ProvidedImplementation.receiveReport(ProvidedImplementation.java:19)
at dsa.speedcamera.ProvidedImplementation.demonstration(ProvidedImplementation.java:73)
at dsa.speedcamera.ProvidedImplementation.main(ProvidedImplementation.java:92)
Process finished with exit code 1
This is the Report file:
package dsa.speedcamera;
public class Report {
public TimeID timeID;
public int speedLimit;
public String carRegistration;
public int speedRecorded;
/**
* Returns a String representing the values in this object so that is can
* be directly printed with System.out.println();
*/
public String toString() {
return timeID.toString() +
"\nSpeed limit: " + speedLimit + " mph" +
"\nCar registration: " + carRegistration +
"\nSpeed recorded: " + speedRecorded + " mph";
}
}
What should I do?
For user-defined objects, you cannot directly add to TreeSet. You need to give TreeSet an attribute on which sorting should be applicable. For example, are you sorting based on speedLimit or speedRecorded? How do I know? How does TreeSet know?
To give TreeSet that information you need to provide what we call a custom Comparator.
Please go through the following articles, they'll help you.
https://www.geeksforgeeks.org/treeset-comparator-method-in-java/
https://www.callicoder.com/java-treeset/
Related
Is there a way to get hold of the parent object of the offending property? I need to use some other properties to build the error message. There is a BindingResult.getTarget() method, but my 'target' object is pretty big with deep nesting and getting to the immediate parent appears daunting.
Consider the following:
public class MyPojo {
#Size(max = 40, message = "Valid length is between 1 - 40")
private String aProperty;
}
// A large complex object with lots of nesting....
#Valid
public class MyJsonRequest {
//
// Elided for brevity
//
#Valid
private MyPojo myPojo;
// yet more objects
}
I can get the MyJsonRequest object by:
BindingResult.getTarget()
Is there a way to get MyPojo?
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public MyResponse errorHandler(MethodArgumentNotValidException ex) {
ex.getBindingResult().getTarget(); // MyJsonRequest
ex.getBindingResult().getFieldErrors().forEach((error) -> {
String defaultMessage = error.getDefaultMessage();
// get the enclosing object where error occured, the MyObject
error.getEnclosingObject() // SOMETHING LIKE THIS?
});
return new Blah();
}
Since I've added an integer to my Schedule class, Gson is throwing an error on some devices: java.lang.IllegalStateException: Expected an int but was BEGIN_ARRAY at line 1 column Y (e.g. column 112 or 120 etc). I looked at this and this answer, which seems to suggest Gson is expecting an int but is getting a BEGIN_ARRAY char, but I have no idea why this would happen after the refactor of adding an extra int to the class.
Before this, my code to parse the list of Schedule objects from a stored Json string was working perfectly fine. I added the Since annotation because of the exception being thrown. Here's the Schedule class:
public class Schedule {
/**
* Added this variable
*/
#Since(1.1) private int addedVar;
/**
* All other variables have the #Since(1.0) annotation
*/
#Since(1.0) all other vars;
}
The function to parse the schedules:
public static ArrayList<Schedule> schedulesFromJson(String schedulesJson) {
Type listType = new TypeToken<ArrayList<Schedule>>(){}.getType();
Gson gson = new Gson();
try {
return gson.fromJson(schedulesJson, listType);
} catch (Exception exception) {
// Try to use the previous version of the schedule, because of IllegalStateException
gson = new GsonBuilder().setVersion(1.0).create();
return gson.fromJson(schedulesJson, listType);
}
}
The strange thing is: on some devices (like my own test devices), this crash never happened. Because of the crash, I added the Since annotation with the try and catch clause, since I expected it might have to do with the extra integer being added and could prevent that by simply reading in the old Schedule version, but this is still throwing the same exception in the catch clause.
Any help with why this is happening?
Figured it out: Because Proguard wasn't set up to not obfuscate the Schedule object (thanks #Marcono1234 for tipping me in the right direction), the Schedule object was stored in storage as an obfuscated object ({"a":true,"b":"Name","c":[true,true,true,true,true,false,false], etc}) instead of using the variable names.
The Exception was thrown because, based on the Schedule class structure before adding the addedVar, there was an array in the schedule. Easier with example.
The old schedule class:
public class Schedule {
private boolean isActive;
private String scheduleName;
private boolean[] days;
private final long timeCreated;
private ArrayList<String> list;
}
The new schedule class:
public class Schedule {
private boolean isActive;
private String scheduleName;
private boolean[] days;
private final long timeCreated;
private int addedVar; // <-- Here it goes wrong
private ArrayList<String> list;
}
Because of adding the int variable before the ArrayList<String> list, when Gson tried to deserialize the stored JSON String, it expected to see an int (the addedVar, but instead saw BEGIN_ARRAY, from the list.
I fixed it by placing the `addedVar`` after the list in my Schedule class, so the Since(1.0) annotation will properly work.
I would like to newer have nulls for my fields which are type of "list"
As I understead below are points where object are initializated, so in these we should do something to initializate empty list in case of null.
Controller (When object is comming from frontend)
Initialization (new AnyObject() or AnyObject.toBuilder - lombok)
FeginClient - Calls between Apis
Is there some framework/annotation which in case of null will set empty list?
Below is what I have currently done
public class TestMapin {
public static void main(String[] args) throws IllegalAccessException {
Test test = new Test();
notNull(test);
System.out.println(test);
}
public static void notNull(Object test) throws IllegalAccessException {
for (Field field : test.getClass().getDeclaredFields()) {
boolean access = field.canAccess(test);
field.setAccessible(true);
if (field.get(test) == null) {
if (field.getType().isAssignableFrom(List.class)) {
field.set(test, Collections.emptyList());
} else if (field.getType().isAssignableFrom(Map.class)) {
field.set(test, Collections.emptyMap());
}
} else if (field.getType().getPackageName().contains("org.owozniak.selfestem")) {
notNull(field);
}
field.setAccessible(access);
}
}
}
#Setter
#ToString
class Test {
private ArrayList<String> stringList;
private Box box = Box.builder().build();
private Magazine magazine;
}
So, I need to use
- initializating via toBuilder - #Singular annotation
- Controllers/Feign clients - inject this code snippet to spring filters
- Initialization via constructor - Use static factory method which will use this code snipped and return "enchanced" instance
Any more ideas/frameworks?
I suggest to use #Singular annotation along with #Builder. This will initialize collection with a non-null List. By annotating one of the parameters (if annotating a method or constructor with #Builder) or fields (if annotating a class with #Builder) with the #Singular annotation, lombok will treat that builder node as a collection, and it generates 2 ‘adder’ methods instead of a ‘setter’ method. One which adds a single element to the collection, and one which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. A ‘clear’ method is also generated. You can read full details on my recent article https://technicalsand.com/using-lombok-advanced-features/
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
#Builder
public class BuilderExample {
#Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
#Singular
private Set<String> occupations;
}
I am trying to get IZO-809 certification I was reading the OCA/OCP SE8 test book and a code really caught my attention.
The code gets me to this question.
I know consumer get a parameter and not return nothing and Supplier has not parameters and returns a value.
But this code is almost the same after the ->.
public class Pregunta24{
private final Object obj;
public Pregunta24(final Object obj){
this.obj = obj;
}
}
//Returns a Supplier
private final Supplier<Pregunta24>supplier = ()->new Pregunta24("HI");
//Returns a Consumer.
private final Consumer<Pregunta24>consumer = a->new Pregunta24(a);
Both codes work.
But if this code not work i know that consumer doesn't return nothing.
private final Consumer<String>consumerString = String::length
I know this not work because consumer doesn't return a value my question is in the supplier code and the consumer code the code is right after the -> mark but this time is considered return in fact a instance of the class.
My question is why sometimes Java complaints that is a return value and something not?
I mean this code.
private final Supplier<Pregunta24>supplier = ()->new Pregunta24("HI");
// I would think is returning a instance of the Pregunta24 class.
private final Consumer<Pregunts24>consumer = a->new Pregunta24(a);
Is returning the same after the -> but why in the consumer don't say the error.
incompatible types: bad return type in lambda expression
But if do this I do
final Consumer<String>consumerString = a->1;
I think the code after the -> is context inferred.
According to javadoc Consumer:
Represents an operation that accepts a single input argument and
returns no result.
Consumer<Pregunts24>consumer = a->new Pregunta24(a);
doesn't actually return anything. This basically is implementation of Consumer#accept method, which accepts an object of type T and has void as return type.
public void accept(Pregunta24 a) {
new Pregunta24(a);
}
You are not returning anything. Same thing with
Consumer<String>consumerString = String::length
public void accept(String a) {
a.length();
}
However
Consumer<String>consumerString = a->1;
is an invalid expression which is translated to something like this:
public void accept(String a) {
1;
}
I have to consume a REST api which follows a common syntax across all retrievable objects:
baseUrl + domainObjectName + qualifier
E.g.
"http://myweb.com/api/" + "cities" + "/{id}"
I created a BaseDao for my data layer and I would like to set up in DAO instantiation the base url for each domain object (baseUrl + domainObjectName). The problem is I have my api Base url defined in the properties file (and would like to keep it that way), and it is not available in the DAO constructor.
This is what I have:
public abstract class BaseDao {
protected static final String ID_QUALIFIER = "/{id}";
protected String domainObjectName = "";
protected String doBaseUrl = "";
#Value("#{config['baseUrlRest']}")
public String apiBaseUrl;
public GenericDaoRestImpl(String domainObjectName) {
this.domainObjectName = domainObjectName;
this.doBaseUrl = apiBaseUrl + domainObjectName;
}
}
When my dao is instantiated, apiBaseUrl is still null, although after creation it is indeed injecting the baseUrl property.
Is there any way around this, like injecting the property as a static constant?
This happens because Java doesn't allow to set fields of a class before the constructor is called. So Spring can't inject the value. There are two solutions:
Pass the value to the constructor instead (example 1)
Use #PostConstruct (example 2)
Example 1:
public GenericDaoRestImpl(
#Value("#{config['baseUrlRest']}") String apiBaseUrl
String domainObjectName
) {
...
}
Example 2:
#Value("#{config['baseUrlRest']}")
public String apiBaseUrl;
public GenericDaoRestImpl(String domainObjectName) {
this.domainObjectName = domainObjectName;
}
#PostConstruct
public void init() {
this.domainObjectName = domainObjectName;
this.doBaseUrl = apiBaseUrl + domainObjectName;
}
I prefer the #PostConstruct because constructor injection eventually leads to constructors with many parameters which makes them unwieldy.
If you don't like it, your third option is using the builder pattern with a fluent interface.