I am currently making an assignment for Java but I am stuck. I have to make a birthdate from the three parameters: day, month and year, which are numbers = int. With this I have to put in some checks for valid dates. That part I think is done, but I get stuck at the following:
I want an if statement to check the day, and if the day is correct, this block of code should be run trough
if (dag >=1 && dag <=31)
{
datum = dag;
}
datum Is a String, because I want to get the date like this: DD-MM-YYY
And dag is an Int. So whenever I try to compile this, BlueJ gives an error at this part saying "incompatible types". I assume this is because I try to place a Int in a String. Is this possible in any way, because I can't find out how.
Use String.valueOf method to convert int to string: -
int i = 32;
String str = String.valueOf(i);
And of course follow the advice in #Brian's answer as to what you should rather do in your case.
Don't make it a string. it's not. I think you should
create a Date object to represent your date (day/month/year combined)
use SimpleDateFormat to print that date out in the appropriate format
That's the proper OO way to do it. Otherwise you end up with a bunch of disparate disconnected variables representing in their combination some object type, but you can't manipulate them atomically, invoke methods on them etc. Holding everything as strings is known as stringly-typing (as opposed to strongly-typing) and is a particularly bad code smell!
At some stage check out Joda-Time for a better date/time API than those suggested above. However for the moment I suspect you've got enough on your plate without downloading extra jars.
Related
I'm trying to convert hard-coded formatting to a Java Units API implementation.
The existing code outputs (for this example, the temperature value in degrees) with two decimal places. For example, 38.70°C. While I'd like to allow the user to specify their own formatting code (which is the end-goal of the change), I think it would be useful to keep the legacy behavior to give people a chance to migrate.
The existing code looks like:
return String.format("%.2f\u00B0C", this.temperature);
The code I'm trying to use looks like:
DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance();
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
NumberDelimiterQuantityFormat formatter =
NumberDelimiterQuantityFormat.builder()
.setNumberFormat(numberFormat)
.setDelimiter("")
.setUnitFormat(SimpleUnitFormat.getInstance())
.build();
return formatter.format(temperature);
It does format, but not with the specified precision. I'd expect 38.70°C but instead get 38.70000076293945℃.
If I just do
numberFormat.format(temperature.getValue().floatValue());
then it does format correctly ("38.70"). So I think the DecimalFormat is basically OK.
I considered just manually building my formatting. However that doesn't really work for what I want to do - pass in the NumberDelimiterQuantityFormat (or applicable interface).
Can anyone suggest an appropriate way to format a Quantity<> with fixed decimal precision?
First off, I'm completely unfamiliar with the Java Unit API and this implementation, but this seemed like an interesting question, so I looked into it.
I had a look at the implementation of NumberDelimiterQuantityFormat and right there in the implementation of the format method it modifies the maxiumFractionDigits of the NumberFormat depending on the fraction
if (quantity != null && quantity.getValue() != null) {
fract = getFractionDigitsCount(quantity.getValue().doubleValue());
}
if (fract > 1) {
numberFormat.setMaximumFractionDigits(fract + 1);
}
Source
This makes little sense to me for two reasons:
It negates the whole reason to have a NumberFormat in the first place especially in context with floating point numbers where it's virtually impossible to avoid superfluous fraction digits.
It modifies the internal state of the NumberDelimiterQuantityFormat in a method where it isn't expected.
I should have checked first, but there is actually an issue about this, which is "being analyzed" for several months now. Maybe it would make sense to ask in there.
Calling the service from Android passing LocalDateTime type parameter.I'm getting this expection while casting request parameter which was send as same Type(LocalDateTime) to LocalDateTime.
here is what i'm doing
#PostMapping(value = "/getDates")
public Object getDates(#RequestBody Map<Object, Object> request) {
LocalDateTime startDate = (LocalDateTime) request.get("fromDate");
LocalDateTime endDate = (LocalDateTime) request.get("toDate");
}
API Request
{
"fromDate":LocalDateTimeObject
"toDate":LocalDateTimeObject1
}
The cast operator does 3 different things: Primitive conversion, typecheck, and type assertion.
These 3 things are like guns and grandmas: Utterly, completely, entirely unrelated. They look similar. It ends there.
The only one of those 3 that actually converts things, is primitive conversion. As the name indicates, this one happens if the thing int the parentheses is a primitive type, such as 'int'.
In other words, (LocalDateTime) foo is typecheck: It checks if foo is a LocalDateTime. If yes, nothing happens. If no, a ClassCastException is thrown.
It looks like you expect it to convert the "fromDate" parameter to a LocalDateTime - a cast is not how you can accomplish this.
JSON can only represent booleans, numbers, strings, arrays, and maps. It cannot represent date time objects. That means that your API should define how to do this. The usual way is via string, by putting in the spec that datetimes are passed for example as: "2020-05-20 14:00". Or possibly as a number: Number of milliseconds since 1970, at UTC, then take the local datetime you find there, and that's what I meant. That is a bit more unwieldy, but it's your API, you get to spec it however you want.
From your error, it looks like you've just tossed a LocalDateTime object at a JSON marshaller, which turned it into a convoluted map structure representing the fields of the LDT object.
This is not workable; I strongly suggest you retool this.
Your first step is to figure out what your spec is. Once you've determined this, your second step is how to convert the string or millis-since-epoch you get into an LDT instance (or if you insist, this map structure).
If it's a string with year-month-day, use java.time.format.DateTimeFormatter to make the appropriate pattern (check the constants, one probably is good to go here), and use that: LocalDateTime.parse(request.get("fromDate"), DateTimeFormatter.ISO_LOCAL_DATE_TIME)
If you have epoch millis (I don't recommend this), Instant.ofEpochMilli(Long.parseLong(request.get("fromDate"))).atZone(ZoneOffset.UTC).toLocalDateTime().
Use the same library (presumably, GSON or jackson) to demarshal the map that you used to marshal it. Whatever the client sent you? They are using one of those two libraries. Use the same and reverse the operation.
I strongly recommend option #1.
using bug finder plugin, I found this bugs but does not understand why it was seen as bug in the code. Does anybody know and give me proper explanation regarding these? Thanks.
Source Code - https://drive.google.com/open?id=1gAyHFcdHBShV-9oC5G7GeOtCGf7bXoso;
Patient.java:17 Patient.generatePriority() uses the nextDouble method of Random to generate a random integer; using nextInt is more efficient [Of Concern(18), Normal confidence]
public int generatePriority(){
Random random = new Random();
int n = 5;
return (int)(random.nextDouble()*n);
}
ExaminationRoom.java:25 ExaminationRoom defines equals and uses Object.hashCode() [Of Concern(16), Normal confidence]
public boolean equals(ExaminationRoom room){
if (this.getWaitingPatients().size() == room.getWaitingPatients().size()){
return true;
}
else {
return false;
}
}
ExaminationRoom.java:15 ExaminationRoom defines compareTo(ExaminationRoom) and uses Object.equals() [Of Concern(16), Normal confidence]
// Compares sizes of waiting lists
#Override
public int compareTo(ExaminationRoom o) {
if (this.getWaitingPatients().size() > o.getWaitingPatients().size()){
return 1;
}
else if (this.getWaitingPatients().size() < o.getWaitingPatients().size()){
return -1;
}
return 0;
}
Hospital.java:41 Bad month value of 12 passed to new java.util.GregorianCalendar(int, int, int) in Hospital.initializeHospital() [Scary(7), Normal confidence]
doctors.add(new Doctor("Hermione", "Granger", new GregorianCalendar(1988, 12, 10), Specialty.PSY, room102));
Person.java:29 Return value of String.toLowerCase() ignored in Person.getFullName() [Scariest(3), High confidence]
public String getFullName(){
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
lastName.toLowerCase();
Character.toUpperCase(lastName.charAt(0));
return firstName + " " + lastName;
}
Don’t create a new Random object each time.
Use random.nextInt(n).
Define a hashCode method in ExaminationRoom.
Having your compareTo method be inconsistent with equals() may or may not be OK.
Use LocalDate instead of GregorianCalendar.
Pick up and use the return values from String.toLowerCase() and Character.toUpperCase().
Consider SpotBugs as a newer alternative to FindBugs.
Details
Random
Creating a new Random object each time you need one gives your poorer pseudo-random numbers with a high risk of numbers being repeated. Declare a static variable holding the Random object outside your method and initialize it in the declaration (Random is thread-safe, so you can safely do that). For drawing a pseudo-random number from 0 through 4 use
int n = 5;
return random.nextInt(n);
It’s not only more efficient (as FindBugs says), I first of all find it much more readable.
hashCode()
#Override
public int hashCode() {
return Objects.hash(getWaitingPatients());
}
compareTo()
The equals method you have shown us seems to contradict FindBugs here. It does look a bit funny, though. Are two waiting rooms considered the same if they have the same number of waiting patients? Please think again. If you end up deciding that they are not equal but should be sorted into the same spot without discrimination, your compareTo method is inconsistent with equals(). If so, please insert a comment stating this fact. If you want FindBugs not to report this as a bug in subsequent analyses, you have two options:
Insert an annotation telling FindBugs to ignore the “bug”.
Create a FindBugs ignore XML file including this point.
I’m sorry I don’t remember the detauls of each, but your search engine should be helpful.
Don’t use GregorianCalendar
The GregorianCalendar class is poorly designed and long outdated. I suggest you evict it from your code and use LocalDate from java.time, the modern Java date and time API, instead.
doctors.add(new Doctor("Hermione", "Granger", LocalDate.of(1988, Month.DECEMBER, 10), Specialty.PSY, room102));
String.toLowerCase()
This was already treated in the other answer. Changing a name to have the first letter in upper case and the rest in lower case is not as simple as it sounds.
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
The first of these two lines doesn’t modify the string firstName because strings have been designed to be immutable and toLowerCase() to return a new string with all letters in lower case (according to the rules of the default locale of the JVM, confusing). The second line also doesn’t modify any character because Java is call-by-value (look it up), so no method can modify a variable passed as argument. You’re not even passing a variable, but the return value from a different method. Also Character.toUpperCase() returns a new char in lower case.
What you need to do is pick up the values returned from these two method calls, use a substring operation for removing the first letter from the lower-case version of the name and concatenate the upper-case version of that letter with the remainder of the lower-case string. If it’s complicated, I am sure that your search engine can find examples of where and how it is done.
A bit of an aside: You may want to think twice before forcing Dr. Jack McNeil to be written as Mcneil and Dr. Ludwig von Saulsbourg as Von saulsbourg.
SpotBugs
It’s only something I have heard, I haven’t checked myself. The source code of FindBugs has been taken over by a project called SpotBugs. They say that SpotBugs is being developed more actively than FindBugs. So you may consider switching. I am myself a happy SpotBugs user in my daily work.
Links
What issues should be considered when overriding equals and hashCode in Java?
Documentation of Comparable explaining what it means that compareTo() is inconsistent with equals() and how to go about it.
Oracle tutorial: Date Time explaining how to use java.time.
Chicago Hope listing Jack McNeil as Orthopedic Surgeon.
Dr. Jack mentioning Dr. Ludwig von Saulsbourg on the cast.
SpotBugs
First thing to remember about "bug finder" tools, is that they are usually only guidelines. With that said:
The class GregorianCalendar counts months from 0, meaning 0 is January, 11 is December. 12 represents the 13th month which doesn't exist. Since the function expects an int, and you gave it an int, no compiler error is generated, even though this is certainly a bug. This article does a good job explaining the reasons to upgrade, and give examples of how to use the new APIs: https://www.baeldung.com/java-8-date-time-intro
If in doubt, you can always check the documentation. In this case, the class Calendar (which GregorianCalendar extends) delcares a static constant public static final int JANUARY = 0; This confirms that january is indeed 0, but also indicates that we can use this constant in our code. You might find new GregorianCalendar(1988, Calendar.JANUARY, 10) to be a bit more readable.
You may also want to consider switching to the more modern and standard systems used to deal with time. The Java 8 Time libraries are the "new standard", and are definitely worth looking into.
Secondly, Strings are immutable in Java. This means that once a String is created, its value can never be changed. This may be counter to your intuitions, as you may have seen code such as:
String s = "hello";
s = s + " world";
However, this doesn't modify the string s. Instead, s + " world" creates a new String, and assigns it to the variable s.
Similarly, s.toLowerCase() doesn't change what s is, it only generates a new String which you must assign.
You probably want firstName = firstName.toLowerCase();
With your first example, nothing immediately jumps out to me as "bad", but if you look at the messages generated by your tool, they label the first example as "Of Concern", but label the others (e.g. the string.toLowerCase() example) as "Scary"/"Scariest". Although I am not familiar with this tool in particular, I imagine this is indicating more of a "Code Smell", rather than an actual bug.
Perhaps look into Unit Testing, if you want to reassure yourself that your code works.
I am having an issue where I believe I have to combine several concepts that I am reading about, but I can't seem to figure out a way to combine them that makes sense, or what would be most efficient. Here is my situation:
I am working with microservices which is why this is so oddly specific. Essentially I am getting a list of Volume objects from another service. A volume object contains three fields, a String type, a String date(which actually represents time in the format "HH:mm", blame the data modelers for the naming conventions), and an int close.
What I am trying to do is to take this list of objects and sort by date(time), then create a new list of objects which will contain the same dates(times), but will have different field values based off of calculations I am doing. In order to sort by time, I believe I need to convert the time fields to Date objects and then compare those. I'm struggling how to iterate through the objects and compare the converted fields. Something that I feel like has put me on the right path is this: How to sort a List<Object> alphabetically using Object name field
But I can't seem to get that to work. Here's my code:
volumeResources.sort(volumeResources, new Comparator<VolumeResource>(){
#Override
public int compare(VolumeResource v1, VolumeResource v2) {
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
try {
Date d1 = format.parse(v1.getDate());
Date d2 = format.parse(v2.getDate());
} catch (ParseException e) {
log.error("Error in parsing string to date. ", e);
}
return d1.compareTo(d2);
}
});
Now right off the bat, I know that this must be incorrect because I started comparing the VolumeResources v1 and v2, but instead tried to compare the dates at the end. In place of the return statement above, I have also tried the one below, but I don't think that will always work because it's not actually setting the formatted object to a Date object:
return format.parse(v1.getDate()).compareTo(format.parse(v2.getDate()));
So that's where I'm lost. This seems like a fairly simple need when described in pseudocode, but I can't seem to figure out how to make it work functionally. Bonus points if there's a way I can populate the other fields I need easily from this. Double bonus points for using lambdas or helping me make this efficient.
Thanks for any and all help everyone.
If you are using Java 8 and above the sorting by time (which is represented as String) may look like this:
volumeResources.sort(new Comparator<VolumeResource>() {
#Override
public int compare(VolumeResource v1, VolumeResource v2) {
return v1.getDate().compareTo(v2.getDate());
}
});
Prior Java 8 it should look like:
Collections.sort(volumeResources, new Comparator<VolumeResource>() {
#Override
public int compare(VolumeResource v1, VolumeResource v2) {
return v1.getDate().compareTo(v2.getDate());
}
});
Using lambda:
volumeResources.sort((v1, v2) -> v1.getDate().compareTo(v2.getDate()));
Assuming that volumeResources is a populated mutable List (not null) and all entries have date field populated properly with a well formed time (no spaces, tabs, or missing leading 0)
Explanation
According to your definition of date field it is naturally sortable as a string, you do not need to convert them into java Date class.
Your example has an issue by using Java 8 sort method defined in List interface in a manner how it used to be prior Java 8 by using utility class Collections - this is wrong, you do not need to supply collection as a first parameter.
A date in Java must have year, month, day.
But you don't have such values, you have only HH:mm (hours, minutes).
Using dates is not appropriate here.
(And calling these values "date" is also strange, I suggest to rename.)
Notice that well-formed HH:mm can be sorted alphabetically to order correctly. So all you need to do is ensure that the input times follow \d\d:\d\d pattern. If you get for example 9:15 then add a padding 0 at the left. Something like this:
volumeResources.sort(volumeResources, new Comparator<VolumeResource>() {
#Override
public int compare(VolumeResource v1, VolumeResource v2) {
return sanitized(v1.getDate()).compareTo(sanitized(v2.getDate());
}
});
The sanitized method is for you to implement accordingly.
If the values are already in the correct format, of type String, then you can drop these calls and simplify to:
return v1.getDate().compareTo(v2.getDate();
Using lambda:
volumeResources.sort(volumeResources, (v1, v2) -> sanitized(v1.getDate()).compareTo(sanitized(v2.getDate()));
I wonder if it's possible to parse any string (at least to try) to sql Date without specifing the string format? In other words I want to make a generic method who take as input a string and return an sql Date.
For instance I have:
String date1="31/12/2099";
String date2="31-12-2099";
and call parseToSqlDate(date1) and parseToSqlDate(date2) which will returns sql dates.
Short answer: No
Why: Parsing any string to a valid date is a task you as an intelligent being could not do (there is no "logical" way to determine the correct date), so you cannot "tell" a computer(program) to do that for you (see JGrice's comment, and there we still have 4-digit years).
Long answer: Maybe, if you are willed to either take risks or do not need a high rate of success.
How:
Define your minimal (format) requirements of a date. E.g. "a minimal date contains 1-8 numbers; 01/01/2001 , 01-01-01 , 01.01 (+current year) , 1.1 (+current year), 1 (+current month + current year) and/or "..contains 1-6 numbers and the letters for months"; 01-Jan-2001 and so on.
Split the input along any non-number/non-month-name characters, with a regex like [^0-9a-zA-Z] (quick thought, may hold some pitfalls)
You now have 1 to 3 (actually more if e.g. the time is included) separate numbers + 1 month name which can be aligned for year/month/day any way you like
For this "alignment", there are several possibilities:
Try a fixed format at first, if it "fits", take it, else try another (or fail)
(only of you get more than one entry at a time) guess the format by assuming all entries have the same (e.g. any number block containing values > 12 is not a month and > 31 is not a day)
BUT, and this is a big one, you can expect any such method to become a major PITA at some point, because you can never fully "trust" it to guess correctly (you can never be sure to have missed some special format or introduced some ambiguous interpretation). I outlined some cases/format, but definitely not all of them, so you will refine that method very often if you actually use it.
Appendix to your comment: "May be to add another parameter and in this way to know where goes day , month and so on?" So you are willed to add "pseudo-format-string" parameter specifying the order of day, month and year; that would make it a lot easier (as "simply" filtering out the delimiters can be achieved).