My class details attributes of restaurants downtown, said attributes being x/y locations and rank. The problem is, whenever I run the program It throws an error, saying that non-abstract class "Downtown" does not override abstract method "compareTo". I cannot make this class abstract because I need to initialise the object outside this block of code. Where does my program go wrong? Is there a problem with my compareTo implementation? Any suggestions will be much appreciated.
public class Downtown implements Comparable<Downtown> {//Throws error on this line
private int x;
private int y;
private int rank;
public Downtown(int x, int y, int rank) {
this.x = x;
this.y = y;
this.rank = rank;
}
//Appropriate setters and getters for x , y and rank
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public int compareTo(Downtown p1, Downtown p2)//Actual comparison
{
// This is so that the sort goes x first, y second and rank last
// First by x- stop if this gives a result.
int xResult = Integer.compare(p1.getX(),p1.getX());
if (xResult != 0)
{
return xResult;
}
// Next by y
int yResult = Integer.compare(p1.getY(),p2.getY());
if (yResult != 0)
{
return yResult;
}
// Finally by rank
return Integer.compare(p1.getRank(),p2.getRank());
}
#Override
public String toString() {
return "["+x+' '+y+' '+rank+' '+"]";
}
Java's Comparable<T> interface defines compareTo method as follows:
int compareTo(T o);
This means that the method must take one parameter; the other parameter is the object itself, i.e. this. You need to implement this one-argument method in place of your two-argument method to fix this problem.
Compiler will help you figure out issues like this by using #Override annotation on your method:
#Override // Issues an error
public int compareTo(Downtown p1, Downtown p2)
#Override // Compiles fine
public int compareTo(Downtown other)
The compareTo method should compare the current object (this) to just one other. It shouldn't have two parameters for comparison. You could write your method like this.
public int compareTo(Downtown p2)//Actual comparison
{
// This is so that the sort goes x first, y second and rank last
// First by x- stop if this gives a result.
int xResult = Integer.compare(getX(),p2.getX());
if (xResult != 0)
{
return xResult;
}
// Next by y
int yResult = Integer.compare(getY(),p2.getY());
if (yResult != 0)
{
return yResult;
}
// Finally by rank
return Integer.compare(getRank(),p2.getRank());
}
Notice how I've replace all the calls on p1 to calls on the current object.
Related
I have a project with two java files. One is the class withe the main-method and the other is a interface with two methods, which is impelemented in the Java-class and I did override the functions there.
This is my Code from the Java-Class:
public class Point implements Compare {
int y;
int x;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public void setX (int x) {
this.x = x;
}
public void setY (int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public boolean isEqualTo(Point x) {
if ((this.x == x.getX()) && (this.y == x.getY()))
return true;
else
return false;
}
public boolean isSmallerThan(Point x) {
if (this.x < x.getX())
return true;
else if (this.y < x.getY())
return true;
else
return false;
}
public static void main(String[] args) {
System.out.println("Test");
}
}
And this is the Code from my Interface:
public interface Compare {
public boolean isEqualTo();
public boolean isSmallerThan();
}
When I try to run the code i always get the following error:
Point.java:1: error: Point is not abstract and does not override abstract method isSmallerThan() in Compare
public class Point implements Compare {
^
1 error
The strange thing is now, that the same code works when i write it in project in IntelliJ IDEA.
I havent found anything on the internet yet.
ah, and i work on macOS.
Hopefully anybody can help, why the code doesnt work in VSC.
Thanks
Actually, it's not Visual Studio Code. It's a Java compiler.
It tells you "If you declared interface methods and then implementing class with this interface you have to implement them or use abstract class. The method signature should be the same."
I am going through the Effective Java 3rd edition and I was reading Item 10: Follow Equals contract when overriding.
There is an example there which I was trying to simulate on my machine. Below is the code for the same.
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Point))
return false;
Point p = (Point)obj;
return (x == p.x) && (y ==p.y);
}
// Use this for demonstration with AtomicPoint class.
/*#Override
public boolean equals(Object obj) {
if(obj == null || (obj.getClass() != getClass())) {
return false;
}
Point p = (Point)obj;
return p.x == x && p.y == y;
}*/
}
public class AtomicPoint extends Point{
private static final AtomicInteger counter = new AtomicInteger();
public AtomicPoint(int x, int y) {
super(x, y);
counter.incrementAndGet();
}
public static int getCounter() {
return counter.get();
}
private static Set<Point> sampleSet = new HashSet<>();
public static void main(String[] args) {
sampleSet.add(new Point(1,2));
sampleSet.add(new Point(1,3));
sampleSet.add(new Point(1,4));
AtomicPoint ap = new AtomicPoint(1,3);
// This should return true but its returning false
System.out.println(sampleSet.contains(ap));
}
}
As you can see from the comment in the AtomicPoint class, I am getting false for the contains check, whereas Joshua Bloch states that this should return true. Can someone help me here?
For using HashSet<T> or HashMap<T> you need to override hashCode() methods from super class. You should have in your editor automatically generating hashCode() and equals() methods (and i'm suggesting you to use that always in every class). If you want to use TreeSet<T> or TreeMap<T> you will need to implement Comparable or Comparator<T> interface and override their compare() or compareTo() methods for using it.
I tried to declare two variable x and y, then create constructor for them and getters with setters. So, for this I used class Distance, while for the obtaining shape I need another class.
package com.company;
import java.lang.Math;
public class Point {
//fields
private int x;
private int y;
//constructor
public Point(int x, int y) {
this.x = x;
this.y = y;
}
//method
//getters
public int getX() {
return x;
}
public int getY() {
return y;
}
//setters
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getPoint(){
}
//function distance
public void distance() {
//**here I need somehow use only two variables instead of four**
double res = Math.sqrt((Math.pow(getX1(), 2) - Math.pow(getX2(), 2))
+ (Math.pow(getY1(), 2) - Math.pow(getY2(), 2)));
System.out.println(res);
}
}
Create a function that accepts object of type Point. The function returns the distance between the original point and passed point
public void distance(Point po) {
//**here I need somehow use only two variables instead of four**
double res = Math.sqrt(
Math.pow(getX() - po.getX(), 2) +
Math.pow(getY() - po.getY(), 2)
);
System.out.println(res);
}
Also your function to calculate distance was wrong.
In the Java 8 tutorial about interface, one example says that when a class implements an interface, one has to type cast the interface type into the class type in order to invoke methods of this class, as shown by the following example from the java 8 tutorial:
public class RectanglePlus
implements Relatable {
public int width = 0;
public int height = 0;
public Point origin;
// four constructors
public RectanglePlus() {
origin = new Point(0, 0);
}
public RectanglePlus(Point p) {
origin = p;
}
public RectanglePlus(int w, int h) {
origin = new Point(0, 0);
width = w;
height = h;
}
public RectanglePlus(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
// a method for moving the rectangle
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
// a method for computing
// the area of the rectangle
public int getArea() {
return width * height;
}
// a method required to implement
// the Relatable interface
public int isLargerThan(Relatable other) {
RectanglePlus otherRect
= (RectanglePlus)other;
if (this.getArea() < otherRect.getArea())
return -1;
else if (this.getArea() > otherRect.getArea())
return 1;
else
return 0;
}
}
In the method isLargerThan(Relatable other), other is casted to type RectanglePlus in order to invoke getArea().
In the other example about default methods in interface, the compareTo(Card o) method doesn't type cast o to type PlayingCard, but can invoke int hashCode() directly, I don't understand this. Thanks for your help.
package defaultmethods;
public class PlayingCard implements Card {
private Card.Rank rank;
private Card.Suit suit;
public PlayingCard(Card.Rank rank, Card.Suit suit) {
this.rank = rank;
this.suit = suit;
}
public Card.Suit getSuit() {
return suit;
}
public Card.Rank getRank() {
return rank;
}
public boolean equals(Object obj) {
if (obj instanceof Card) {
if (((Card)obj).getRank() == this.rank &&
((Card)obj).getSuit() == this.suit) {
return true;
} else {
return false;
}
} else {
return false;
}
}
public int hashCode() {
return ((suit.value()-1)*13)+rank.value();
}
public int compareTo(Card o) {
return this.hashCode() - o.hashCode();
}
public String toString() {
return this.rank.text() + " of " + this.suit.text();
}
public static void main(String... args) {
new PlayingCard(Rank.ACE, Suit.DIAMONDS);
new PlayingCard(Rank.KING, Suit.SPADES);
}
}
In short: Because hashCode is defined in java.lang.Object and every other class extends Object implicitly.
So when you have
public int compareTo(Card o) {
return this.hashCode() - o.hashCode();
}
the compiler already knows that o is of type Card which extends Object which defines a hashCode method. No need for an explicit cast.
On the other hand in your isLargerThan method the parameter is of type Relatable:
public int isLargerThan(Relatable other) {
RectanglePlus otherRect
= (RectanglePlus)other;
if (this.getArea() < otherRect.getArea())
return -1;
else if (this.getArea() > otherRect.getArea())
return 1;
else
return 0;
}
And judging from the link you provided, the getArea method is defined in RectanglePlus only. Since the compiler only sees Relatable it does not know anything about a getArea method at this point and you need to explicitly cast other to RectanglePlus to be able to access it.
Note that you should actually do an instanceof check before casting to avoid a ClassCastException when other is not a RectanglePlus (you don't know if there might be other classes implementing Relatable).
Let me try a non-code related example:
If people have a pet they usually give it a name. So whatever pet you have, one can always ask for its name (cf. hashCode). But they cannot ask you to make it bark (cf. getArea) unless they know that it is a dog.
And you will probably fail to make a cat bark (cf. ClassCastException).
Is there a way to combine the following 2 Statesments?
Map<Integer,Double> collX = listeAllerPunkte.stream().collect(groupingBy(DataPoint::getId,
averagingDouble(DataPoint::getX)));
Map<Integer,Double> collY = listeAllerPunkte.stream().collect(groupingBy(DataPoint::getId,
averagingDouble(DataPoint::getY)));
I have a Class DataPoints like this:
public class DataPoint {
public final double x;
public final double y;
private int Id;
public DataPoint(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getId() {
return Id;
}
}
The Id contains a Random value between 0-5.
listeAllerPunkte is a List with a lot of DataPoints
Now I want to create a DataPoint for each DataPoints in the List with the same Id. The DataPoint should have the average of the x and y values of the Datapoints with the same Id.
With the two Statemantes from the Beginning i must create the DataPoints manually out of the two Maps.
Is there a way to create them directly in the stream?
A general solution would be using a collector which can combine two collectors to process both at once. Unfortunately, such collector does not exist in the standard API, but this answer provide an implementation of such a collector.
Alternatively, you can create a solution for this specific case by creating your own class for holding the summary of points, e.g.
static class DataPointSummary {
long count;
double sumX, sumY;
public double getAverageX() {
return count==0? 0: sumX/count;
}
public double getAverageY() {
return count==0? 0: sumY/count;
}
public void add(DataPoint p) {
count++;
sumX+=p.getX();
sumY+=p.getY();
}
public DataPointSummary merge(DataPointSummary s) {
count+=s.count;
sumX+=s.sumX;
sumY+=s.sumY;
return this;
}
#Override
public String toString() {
return "DataPointSummary["+count+" points"
+", avg x="+getAverageX()+", avg y="+getAverageY()+']';
}
}
Then you may collect your points like
Map<Integer,DataPointSummary> coll = listeAllerPunkte.stream().collect(
groupingBy(DataPoint::getId, Collector.of(
DataPointSummary::new, DataPointSummary::add, DataPointSummary::merge)));
Note that I assumed that you method signature public double getId() is a typo and actually public int getId() as otherwise, the examples in your question won’t work.
The summary implementation above works well if the coordinates of the points have the same magnitude. If you encounter both, very large values and very small values within the same group, you may need a summing with error compensation algorithm. Instead of implementing it yourself, I recommend using the summary implementation of the JRE instead:
static class DataPointSummary {
final DoubleSummaryStatistics x=new DoubleSummaryStatistics();
final DoubleSummaryStatistics y=new DoubleSummaryStatistics();
public double getAverageX() {
return x.getAverage();
}
public double getAverageY() {
return y.getAverage();
}
public void add(DataPoint p) {
x.accept(p.getX());
y.accept(p.getY());
}
public DataPointSummary merge(DataPointSummary s) {
x.combine(s.x);
y.combine(s.y);
return this;
}
#Override
public String toString() {
return "DataPointSummary["+x.getCount()+" points"
+", avg x="+getAverageX()+", avg y="+getAverageY()+']';
}
}
This variant is used the same way as the first one.