14 Replies - 384 Views - Last Post: 15 March 2019 - 12:48 PM Rate Topic: -----

#1 nightraven   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 46
  • Joined: 17-November 17

How to invoke the get() method of Optional objects

Posted 12 March 2019 - 06:54 AM

Hi there. I have a Java assignment of which the task is to implement classes that can be used to represent points and tracks, along with a small program that demonstrates use of the classes.

In the Track class I have implemented a method that reads data from a CSV file, parses it and adds the data to an ArrayList:
List<Point> track = new ArrayList<> ();

Here is the readFile() method:
// readFile method that creates a sequence of point objects from data in a file, the name of which is supplied as a string parameter
    public void readFile(String test) throws FileNotFoundException {

        // Scanner for specified file
        Scanner input = new Scanner(new File(test));
        int iteration = 0;
        track.clear ();

        //Fetch and parse
        while (input.hasNextLine ())  {
            String newLine = input.nextLine ();
            if (iteration == 0) { iteration++; continue;}
                String delimiter = ",";
                String[] line = newLine.split(delimiter);
            if (line.length != 4) {
                throw new GPSException ("File contains illegal number of values.");
            }
            else {
                ZonedDateTime time  = ZonedDateTime.parse (line[0]);
                double longitude = Double.parseDouble (line[1]);
                double latitude  = Double.parseDouble (line[2]);
                double elevation = Double.parseDouble (line[3]);

                Point newPoint = new Point (time, longitude, latitude, elevation);
                track.add (newPoint);
            }
        }
            input.close ();
        }

Along with various methods such as add(), size(), and get() (all self explanatory), I am also implementing two methods to find both the lowest and highest points. For this I'm using the Streams API - but the problem is that the methods have to return Point objects, not Optionals. I know that Optional objects have a get() method that returns the contained object, so using that would fix the observed problem but I do not know how to invoke this method with the code I've already written for the functions:
// Lowest point method
    public Optional<Point> lowestPoint() {
        return track.stream().min (Comparator.comparingDouble (Point::getElevation));
    }

    // Highest point method
    public Optional<Point> highestPoint() {
        return track.stream().max (Comparator.comparingDouble (Point::getElevation));
    }

I also want to add validation to both of these methods, but would appreciate any guidance on how to invoke the get() method correctly so that I can return a Point object instead of an Optional object. Help would be much appreciated.

Is This A Good Question/Topic? 3
  • +

Replies To: How to invoke the get() method of Optional objects

#2 xclite   User is offline

  • I wrote you an code
  • member icon


Reputation: 1354
  • View blog
  • Posts: 4,179
  • Joined: 12-May 09

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 07:20 AM

A thought to get us in the right direction:

What should happen if the Option isn't present? In other words, if it is absent?

Option represents something that might not be there, so when you "unwrap" it, you'll have to handle the case of something being there (a more obvious case) and the case of nothing being contained within the Option.
Was This Post Helpful? 1
  • +
  • -

#3 NormR   User is online

  • D.I.C Lover
  • member icon

Reputation: 757
  • View blog
  • Posts: 5,708
  • Joined: 25-December 13

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 07:24 AM

Quote

invoke the get() method correctly so that I can return a Point object instead of an Optional object

Worded slightly differently:
What should the methods return if the list is empty?

This post has been edited by NormR: 12 March 2019 - 07:26 AM

Was This Post Helpful? 0
  • +
  • -

#4 andrewsw   User is offline

  • Stealth IT
  • member icon

Reputation: 6736
  • View blog
  • Posts: 27,734
  • Joined: 12-December 12

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 07:30 AM

Stepping outside my comfort zone, but wouldn't min and max handle the case of an empty list? In which case I would think

return Optional.of(whatever min gives);


but I'm probably wrong ;)
Was This Post Helpful? 0
  • +
  • -

#5 NormR   User is online

  • D.I.C Lover
  • member icon

Reputation: 757
  • View blog
  • Posts: 5,708
  • Joined: 25-December 13

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 07:48 AM

The min and max methods return Optional objects. The OP has stated that his get methods should return Point objects.
My question is: what should his get methods return if there are no Point objects to search?
Was This Post Helpful? 0
  • +
  • -

#6 nightraven   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 46
  • Joined: 17-November 17

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 09:07 AM

If there were no Point objects to search, would it not just throw a NoSuchElementException, or something in that regard?
Was This Post Helpful? 0
  • +
  • -

#7 xclite   User is offline

  • I wrote you an code
  • member icon


Reputation: 1354
  • View blog
  • Posts: 4,179
  • Joined: 12-May 09

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 10:35 AM

For what it's worth, if I were making this API, I'd probably either return the Option outright, or throw an IllegalArgumentException if an empty stream is passed.

However, your case is that you have an assignment, and need to return the point, so let's examine that.

You've already correctly identified "get".

Your code is like:

return track.stream().max (Comparator.comparingDouble (Point::getElevation));



the expression track.stream().max(...) returns an Optional. So how do you call .get()?


You could do the entire thing in one line, but I might do something like:

  max = track.stream().max(Comparator.comparingDouble (Point::getElevation));
  if (max.isPresent()) {
    // do something to return the point... maybe .get?
  }
  else {
    // we need to handle the empty case. Here you could either return null or throw an IllegalArgumentException or some other "error" situation.
  }



What do you think?
Was This Post Helpful? 0
  • +
  • -

#8 NormR   User is online

  • D.I.C Lover
  • member icon

Reputation: 757
  • View blog
  • Posts: 5,708
  • Joined: 25-December 13

Re: How to invoke the get() method of Optional objects

Posted 12 March 2019 - 01:59 PM

Quote

If there were no Point objects to search, would it not just throw a NoSuchElementException

You are designing the methods. What do you want it to do?
Was This Post Helpful? 0
  • +
  • -

#9 nightraven   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 46
  • Joined: 17-November 17

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 07:41 AM

I have added the validation to the methods, and it passes the provided unit tests (my lecturer provided a set of tests with the assignment). But guys, I will admit...I am very new to Java and this is the first assignment I've been given for my OOP module at university, so please bare with me.

The validation that's being used throughout the majority of the program is one we had created for us, and it's defined as follows:
public class GPSException extends RuntimeException {
  public GPSException(String message) {
    super(message);
  }
}

The problem is, when using either .get() or .else() to return a point object, I'm still running into all sorts of problems. I have create a new Point instance in the class, but the instance is rejected. The code is as follows:
 // Lowest point method
    public Optional<Point> lowestPoint() {
        ZonedDateTime time = ZonedDateTime.now ();
        double longitude = 0;
        double latitude = 0;
        double elevation = 0;
        if (track.size () != 4) {
            throw new GPSException ("Not enough points to compute");
        } else {
            Point lp = new Point (time, longitude, latitude, elevation);
            return track.stream ()
                        .min (Comparator.comparingDouble (Point::getElevation))
                        .orElse (lp);
        }
    }

Was This Post Helpful? 0
  • +
  • -

#10 NormR   User is online

  • D.I.C Lover
  • member icon

Reputation: 757
  • View blog
  • Posts: 5,708
  • Joined: 25-December 13

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 08:01 AM

Quote

running into all sorts of problems.

Please copy the full text of any error messages and paste it here so we can see what is happening.

Would there be a problem in the calling code if a bogus Point that was created in the lowestPoint method is returned vs a real Point?

Why the test for the size of track to be exactly 4?

This post has been edited by NormR: 13 March 2019 - 08:07 AM

Was This Post Helpful? 0
  • +
  • -

#11 nightraven   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 46
  • Joined: 17-November 17

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 08:17 AM

This is the error message: Error:(78, 33) java: incompatible types: Point cannot be converted to java.util.Optional<Point>.

And I left it at 4 because it passed the tests at that value. But now I realise that a track can obviously contain more than four points, so I'm a bit stuck there now...
Was This Post Helpful? 0
  • +
  • -

#12 NormR   User is online

  • D.I.C Lover
  • member icon

Reputation: 757
  • View blog
  • Posts: 5,708
  • Joined: 25-December 13

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 08:26 AM

Quote

Error:(78, 33) java: incompatible types: Point cannot be converted to java.util.Optional<Point>

The method is defined to return an Optional object, however the orElse method returns a Point.
It should compile if the returned type was changed to Point.

Quote

track can obviously contain more than four points, so I'm a bit stuck there

What is the minimum number of Points that works for the program? Would 1 be enough?
If so check the size > 0

If there are Points in track, then the min method is almost guaranteed to find a lowest one and there would be no need for a bogus Point to be made and returned by the orElse method.

This post has been edited by NormR: 13 March 2019 - 08:28 AM

Was This Post Helpful? 1
  • +
  • -

#13 ndc85430   User is offline

  • I think you'll find it's "Dr"
  • member icon

Reputation: 971
  • View blog
  • Posts: 3,829
  • Joined: 13-June 14

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 08:27 AM

As the error says: the method is declared to return an Optional<Point>, but by calling orElse, you're returning a Point. Why do you (think you) need the orElse call?
Was This Post Helpful? 0
  • +
  • -

#14 nightraven   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 46
  • Joined: 17-November 17

Re: How to invoke the get() method of Optional objects

Posted 13 March 2019 - 12:12 PM

View PostNormR, on 13 March 2019 - 08:26 AM, said:

Quote

Error:(78, 33) java: incompatible types: Point cannot be converted to java.util.Optional<Point>

The method is defined to return an Optional object, however the orElse method returns a Point.
It should compile if the returned type was changed to Point.

Quote

track can obviously contain more than four points, so I'm a bit stuck there

What is the minimum number of Points that works for the program? Would 1 be enough?
If so check the size > 0

If there are Points in track, then the min method is almost guaranteed to find a lowest one and there would be no need for a bogus Point to be made and returned by the orElse method.
Yep, I soon realised that I needed to change the return type to Point, and it worked. Also, I changed the validation to size >=0 -> throw exception and it passed the tests also.

Thanks a lot for that.

Ndc, I need that call as the unit tests provided by my lecturer are designed so that the tests to check the higher and lower methods look for a return of a Point object. I know this could have been achieved with a loop instead of the stream API but I thought the 2nd option seemed a bit cleverer.
Was This Post Helpful? 0
  • +
  • -

#15 ccdan   User is offline

  • D.I.C Head

Reputation: 4
  • View blog
  • Posts: 116
  • Joined: 09-December 12

Re: How to invoke the get() method of Optional objects

Posted 15 March 2019 - 12:48 PM

View Postnightraven, on 13 March 2019 - 09:12 PM, said:

I know this could have been achieved with a loop instead of the stream API but I thought the 2nd option seemed a bit cleverer.

You should bear in mind that using functional style just adds complexity, which is often unnecessary and can make your code difficult to read(instead of making it clearer - as it is usually advertised) and quite a bit slower.

You could simply use:
return Collections.max(tracks, Comparator.comparingDouble (Point::getElevation));


That's all! No need for stream and optional and all the additional handling. And performance is a bit better.

This post has been edited by ccdan: 16 March 2019 - 12:00 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1