Can anyone help me compare an Integer to a Double using generics?
This is what I have:
public static <T extends Comparable<? super T>> int compare(T arg1, T arg2)
{
return arg1.compareTo(arg2);
}
public static void main(String[] args)
{
Number i = new Integer(5);
Number j = new Double(7);
System.out.println(GenericsTest.compare(i, j));
}
The error message I get is:
Bound mismatch: The generic method compare(T, T) of type GenericsTest is not applicable for the arguments (Number, Number). The inferred type Number is not a valid substitute for the bounded parameter >
The idea of this solution is to widen to BigDecimal and then compare the two numbers (now is cleaner but somehow formatting doesn't work). Note you may reuse this static comparator without having to cast to double anywhere else. In the implementation you do need conversion to double not to lose information, basically you widen to the most general representation.
private static final Comparator<Number> NUMBER_COMPARATOR = new Comparator<Number>() {
private BigDecimal createBigDecimal(Number value) {
BigDecimal result = null;
if (value instanceof Short) {
result = BigDecimal.valueOf(value.shortValue());
} else
if (value instanceof Long) {
result = BigDecimal.valueOf(value.longValue());
} else
if (value instanceof Float) {
result = BigDecimal.valueOf(value.floatValue());
} else
if (value instanceof Double) {
result = BigDecimal.valueOf(value.doubleValue());
} else
if (value instanceof Integer) {
result = BigDecimal.valueOf(value.intValue());
} else {
throw new IllegalArgumentException("unsupported Number subtype: " + value.getClass().getName());
}
assert(result != null);
return result;
}
public int compare(Number o1, Number o2) {
return createBigDecimal(o1).compareTo(createBigDecimal(o2));
};
};
public static void main(String[] args) {
Number i = Integer.valueOf(5);
Number j = Double.valueOf(7);
// -1
System.out.println(NUMBER_COMPARATOR.compare(i, j));
i = Long.MAX_VALUE;
j = Long.valueOf(7);
// +1
System.out.println(NUMBER_COMPARATOR.compare(i, j));
i = Long.MAX_VALUE;
j = Long.valueOf(-7);
// +1
System.out.println(NUMBER_COMPARATOR.compare(i, j));
i = Long.MAX_VALUE;
j = Double.MAX_VALUE;
// -1
System.out.println(NUMBER_COMPARATOR.compare(i, j));
i = Long.MAX_VALUE;
j = Long.valueOf(Long.MAX_VALUE - 1);
// +1
System.out.println(NUMBER_COMPARATOR.compare(i, j));
// sorting Long values
Long[] values = new Long[] {Long.valueOf(10), Long.valueOf(-1), Long.valueOf(4)};
Arrays.sort(values, NUMBER_COMPARATOR);
// [-1, 4, 10]
System.out.println(Arrays.toString(values));
}
As aready said in the comments, Number does not implement Comparable.
But Double and Integer do.
One way to make this work is like this:
public static <T extends Comparable<? super T>> int compare(T arg1, T arg2)
{
return arg1.compareTo(arg2);
}
public static void main(String[] args)
{
Double i = new Integer(5).doubleValue();
Double j = new Double(7);
System.out.println(GenericsTest.compare(i, j));
}
Number doesn't implement Comparable.
Declare both variables as Integer.
private boolean compareObject(Object expected, Object actual) {
if (expected instanceof Number && actual instanceof Number) {
double e = ((Number) expected).doubleValue();
double a = ((Number) actual).doubleValue();
return e == a;
} else {
return com.google.common.base.Objects.equal(expected, actual);
}
}
Create a class that implements Comparable which takes a Number in the constructor.
e.g.
public class GenericNumber implements Comparable<GenericNumber> {
private Number num;
public GenericNumber(Number num) {
this.num = num;
}
// write compare function that compares num member of two
// GenericNumber instances
}
Then simply do this:
GenericNumber i = new GenericNumber(new Integer(5));
GenericNumber j = new GenericNumber(new Double(7));
System.out.println(GenericsTest.compare(i,j));
Related
I would like to generically add numbers in java. I'm running into difficulty because the Numbers class doesn't really support what I want to do. What I've tried so far is this:
public class Summer<E extends Number> {
public E sumValue(List<E> objectsToSum) {
E total = (E) new Object();
for (E number : objectsToSum){
total += number;
}
return null;
}
Obviously this will not work. How can I go about correcting this code so I could be given a list of <int> or <long> or whatever and return the sum?
In order to calculate a sum generically, you need to provide two actions:
A way to sum zero items
A way to sum two items
In Java, you do it through an interface. Here is a complete example:
import java.util.*;
interface adder<T extends Number> {
T zero(); // Adding zero items
T add(T lhs, T rhs); // Adding two items
}
class CalcSum<T extends Number> {
// This is your method; it takes an adder now
public T sumValue(List<T> list, adder<T> adder) {
T total = adder.zero();
for (T n : list){
total = adder.add(total, n);
}
return total;
}
}
public class sum {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(4);
list.add(8);
CalcSum<Integer> calc = new CalcSum<Integer>();
// This is how you supply an implementation for integers
// through an anonymous implementation of an interface:
Integer total = calc.sumValue(list, new adder<Integer>() {
public Integer add(Integer a, Integer b) {
return a+b;
}
public Integer zero() {
return 0;
}
});
System.out.println(total);
}
}
As Number class does not expose interface for performing calculations, the only way to solve this problem is to create classes which encapsulates required operations for each supported numeric type. Than in your class you will need to use specific type.
Number has intValue(), floatValue(), doubleValue(), longValue, and shortValue(). Choose one and use it. For example,
double total;
total += number.doubleValue();
return total;
Also, java generics are in no way equivalent to c++ templates. You can not allocate new instances of a java generic type. This can never work:
E hoot = (E) new Object();
Finally, long, short, int, double, and float are not class types; they are primitive types. As such they are not available for use with Java generics.
below method get numbers such as int, float, etc and calculate sum of them.
#SafeVarargs
private static <T extends Number> double sum(T... args) {
double sum = 0d;
for (T t : args) {
sum += t.doubleValue();
}
return sum;
}
Number doesn't support any operations so you need to cast the values to the types required. e.g. If Number is a BigDecimal, there is no += operator.
You should check runtime type (e.g. using instanceof) and cast to the known type and do appropriate addition operation. Not sure what will be type of result, taking in account that the list could contain a lot of different number types.
since the introduction of Java 8 streams and lambda you can have a shorter solution
public interface Adder {
static <E extends Number> E sumValues(Collection<E> objectsToSum, BinaryOperator<E> sumOp) {
return objectsToSum.stream().reduce(sumOp).orElse(null);
}
}
and use it as follows
int sum = Adder.sumValues(List.of(4, 5, 6, 7), Integer::sum);
Note, that Stream::reduce with just accumulator returns Optional that's why I used orElse(null), but I would recommend to send also zero value as parameter to Adder::sumValue
not elegant, but works
public class GenericSum {
public static <T extends Number> Number sum(T x, T y) throws Exception{
// Field
Field primitiveField = x.getClass().getField("TYPE");
// int|float... object
var primitiveType = primitiveField.get(null);
// cast to class
var adder = x.getClass().getMethod("sum", (Class)primitiveType,(Class)primitiveType);
var result = adder.invoke(null,x, y);
return (Number) result;
}
public static void main(String[] args) throws Exception {
var a1 = 3;
var a2 = 5;
var res = sum(a1, a2);
System.out.println(res);
var b1 = 2.0f;
var b2 = 3.0f;
var res2 = sum(b1,b2);
System.out.println(res2);
}
}
I was looking for any implementation of a generic adder accumulator when I came across this question.
I feel this is a lot messy and ugly for most use cases but if you need a very generic one here it is.
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.function.BinaryOperator;
public class Adder implements BinaryOperator<Object> {
#Override
public Object apply(Object partialSum, Object element) {
// if both are instances of Number, then add them
// otherwise we parse the element as string
// and add it to the partial sum
if(partialSum instanceof Number) {
if(element instanceof Number) {
if(partialSum instanceof Integer)
return (Integer) partialSum + ((Number) element).intValue();
if(partialSum instanceof Long)
return (Long) partialSum + ((Number) element).longValue();
if(partialSum instanceof BigInteger)
return ((BigInteger) partialSum).add(BigInteger.valueOf(((Number) element).longValue()));
if(partialSum instanceof Float)
return (Float) partialSum + ((Number) element).floatValue();
if(partialSum instanceof Double)
return (Double) partialSum + ((Number) element).doubleValue();
if(partialSum instanceof BigDecimal)
return ((BigDecimal) partialSum).add(BigDecimal.valueOf(((Number) element).doubleValue()));
else
throw new NumberFormatException("Unknown number type for partialSum: " + partialSum.getClass());
}
else {
if(partialSum instanceof Integer)
return (Integer) partialSum + Integer.parseInt(element.toString());
if(partialSum instanceof Long)
return (Long) partialSum + Long.parseLong(element.toString());
if(partialSum instanceof BigInteger)
return ((BigInteger) partialSum).add(new BigInteger(element.toString()));
if(partialSum instanceof Float)
return (Float) partialSum + Float.parseFloat(element.toString());
if(partialSum instanceof Double)
return (Double) partialSum + Double.parseDouble(element.toString());
if(partialSum instanceof BigDecimal)
return ((BigDecimal) partialSum).add(new BigDecimal(element.toString()));
else
throw new NumberFormatException("Unknown number type for partialSum: " + partialSum.getClass());
}
}
throw new NumberFormatException("partialSum " + partialSum + " must be of type java.lang.Number but found " + partialSum.getClass());
}
}
Honestly this would've been a lot simpler if generic Number types supported the + operator like how Strings does.
I have to write a method which selects the maximum value from a list, and it has to be with Generics. Obviously the List can be Number and String as well. (The return value has to be Opt Object. This is the tasks.)
This is what I have so far, but its not working, I would appreciate your advice:
public static <T> Opt<T> max(List<? extends Object> list) {
T max = (T) list;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) > max) {
max = (T) list.get(i);
}
}
return (Opt<T>) max;
}
And this is the main looks like: (From this one I have to make my method work.)
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
Utility.addTo(stringList, "aghi");
Utility.addTo(stringList, "fed");
Utility.addTo(stringList, "ghh");
Utility.addTo(stringList, "abc");
Utility.addTo(stringList, "123");
System.out.println("The maximum value: " + Utility.max(stringList).get());
List<Integer> intList = new ArrayList<>();
Utility.addTo(intList, 123);
Utility.addTo(intList, 456);
Utility.addTo(intList, -199);
Utility.addTo(intList, -90);
Utility.addTo(intList, 0);
Utility.addTo(intList, -10);
Utility.addTo(intList, 200);
System.out.println("The maximum value: " + Utility.max(intList).get());
List<Double> doubleList = new ArrayList<>();
Utility.addTo(doubleList, 123.0);
Utility.addTo(doubleList, 456.001);
Utility.addTo(doubleList, -199.0);
Utility.addTo(doubleList, -90.90);
Utility.addTo(doubleList, 0.0);
Utility.addTo(doubleList, -10.20);
Utility.addTo(doubleList, 200.1);
System.out.println("The maximum value: " + Utility.max(doubleList).get());
}
And the Output shoud be:
The maximum value: ghh
The maximum value: 456
The maximum value: 456.001
Using Streams makes it really easy by doing all the work for you:
public static <T extends Comparable<T>> Optional<T> max(List<T> list) {
return list.stream().max(Comparator.naturalOrder());
}
or the Collections class of utility functions:
public static <T extends Comparable<T>> Optional<T> max(List<T> list) {
if (list.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(Collections.max(list, Comparator.naturalOrder()));
}
}
Your code is not working (I mean cannot be compiled) because of this line:
if (list.get(i) > max) {
In Java, there are no overloaded operators as in C++, so you need to find another way...
By Opt you probably meant java.util.Optional class and you can use it like this:
public static <T> Optional<T> max(List<T> list) {
Optional<T> max = Optional.empty();
for (int i = 0; i < list.size(); i++) {
// if (list.get(i) > max) {
max = Optional.of(list.get(i));
// }
}
return max;
}
This is of course not working, it returns last element from list.
When creator of a class expects users might be interested in sorting (comparing), they would implemente java.lang.Comparable, this is the case for String, Long and Double. So instead of T, you can say T extends Comparable like this:
public static <T extends Comparable<T>> Optional<T> max(List<T> list) {
T max = null;
for (int i = 0; i < list.size(); i++) {
final T item = list.get(i);
if (max == null) {
max = item;
} else if (max.compareTo(item) < 0) {
max = item;
}
}
if (max == null) return Optional.empty();
return Optional.of(max);
}
Look at Comparable#compareTo JavaDoc.
Try to understand what this line means exactly (and why you cannot use it with list of java.lang.Objects):
public static <T extends Comparable<T>> Optional<T> max(List<T> list) {
and why we do not need it in addTo:
public static <T> void addTo(List<T> list, T e) {
So this program is attempted to take a command line argument like the following:
S 4 1 2 3 4 4
args[0] is the array type
args[1] is the array length
args[2...] are the values in the array
args[length-1] is a key that will be used in a linear search
public class whatTheFoo{
#SuppressWarnings({ "unchecked", "rawtypes" })
public static <E> void main(String[] args) {
for(int i=0;i<args.length;i++)System.out.print(args[i]);
System.out.println();
int arraySize = Integer.parseInt(args[1]);
E[] array = (E[])new Object[arraySize];
E key = null;
if (args[0].matches("I|i")) {
for (int i = 2; i < args.length-1; i++) {
array[i-2]=(E)new Integer(args[i]);
System.out.println(array[i-2]);
}
key = (E) new Integer(args[args.length-1]);
System.out.println("Key is: " + key);
}
...
if(linearSearch(array, key)<0)
System.out.println("Didnt find it");
else
System.out.println("Found it at index: "+(linearSearch(array, key)-1));
}
public static <E> int linearSearch(E[]array,E key) {
int index=-1;
for(int i=0;i<array.length;i++) {
if(array[i].equals(key)){
index = (int) array[i];
}
}
return index;
}
}
This works, but when I change the linearSearch method to:
public static <E extends Comparable<E>> int linearSearch(E[]array,E key)
I get the error message:
The method linearSearch(E[], E extends Comparable<E>) in the type Prog7b is not applicable for the arguments (E[], E)
but if I change main to:
public static <E extends Comparable<E>> void main(String[] args) {
I get:
Exception in thread "main" I412344java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
at whatTheFoo.main(whatTheFoo.java:10)
The method has been directed to include in the method:
<E extends Comparable<E>>.
Where am I going wrong? Thanks for reading.
-------------------------------------------------------------------
For those that may be curious, this is the end result of all the help supplied. Thanks again!
public class Prog7b {
// #SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
if (args[0].matches("I|i")) {
Integer[] array = new Integer[Integer.parseInt(args[1])];
for (int i = 2; i < args.length - 1; i++) {
array[i - 2] = new Integer(args[i]);
}
Integer key = new Integer(args[args.length - 1]);
if (linearSearch(array, key) < 0) {
System.out.println("Didnt find it");
} else
System.out.println("Found it at index: " + (linearSearch(array, key) - 1));
System.out.println("The max of the array is: " + max(array));
print(array);
} else if (args[0].matches("S|s")) {
String[] array = new String[Integer.parseInt(args[1])];
for (int i = 2; i < args.length - 1; i++) {
array[i - 2] = new String(args[i]);
}
String key = new String(args[args.length - 1]);
if (linearSearch(array, key) < 0) {
System.out.println("Didnt find it");
} else
System.out.println("Found it at index: " + (linearSearch(array, key) - 1));
System.out.println("The max of the array is: " + max(array));
print(array);
} else {
Double[] array = new Double[Integer.parseInt(args[1])];
for (int i = 2; i < args.length - 1; i++) {
array[i - 2] = new Double(args[i]);
}
Double key = new Double(args[args.length - 1]);
if (linearSearch(array, key) < 0) {
System.out.println("Didnt find it");
} else
System.out.println("Found it at index: " + (linearSearch(array, key) - 1));
System.out.println("The max of the array is: " + max(array));
print(array);
}
}
public static <E extends Comparable<E>> int linearSearch(E[] array, E key) {
int index = 0;
for (int i = 0; i < array.length; i++) {
index++;
if (array[i].equals(key)) {
return index;
}
}
return -1;
}
public static <E extends Comparable<E>> E max(E[] list) {
E max = list[0];
for (int i = 1; i < list.length; i++) {
if (max.compareTo(list[i]) < 0) {
max = list[i];
}
}
return max;
}
private static <E> void print(E[] list) {
System.out.print("[");
for (int i = 0; i < list.length - 1; i++)
System.out.print(list[i] + ", ");
System.out.print(list[list.length - 1] + "]\n");
}
}
I don't think main is supposed to be generic. (The <E> part in the method declaration declares a type variable, which makes it generic.) If main is really supposed to be generic, then you need to talk to your teacher because they are doing something weird and we can't really guess about it.
Generics are a compile-time only concept. Basically the idea is that you have some code which is actually somewhat agnostic about particular types, but still need some kind of abstract information about it.
For example, suppose we had some method that checks if an object is null:
Object requireNonNull(Object obj) {
if (obj == null) {
throw new NullPointerException();
} else {
return obj;
}
}
This is fine. We can pass any sort of object to the method. (Integer, String, whatever.) But what if we wanted to assign the return value directly?
We want to be able to do this:
String mightBeNull = ...;
String definatelyNotNull = requireNonNull(mightBeNull);
This makes our validation code neater. (Maybe instead of checking for null, our validation is actually about 10 lines long and we don't want to repeat it all the time.)
Well, as it stands, we can't, because we will get a compile-time error for trying to assign an Object to a String.
Generics let us do this, though:
<T> T requireNonNull(T obj) {
if (obj == null) {
throw new NullPointerException();
} else {
return obj;
}
}
The type parameter <T> says that we declare a sort of temporary type. We don't care about what it actually is, but we can say that the method returns whatever we pass to it. Whatever type obj is at the point that we call requireNonNull, the method returns that type to the caller.
So now we can do this:
String s = requireNonNull("");
Integer i = requireNonNull(10);
Float f = requireNonNull(2f);
And so on.
requireNonNull is actually a real method and that is how it works.
The point, though, is that generics let you write very general API which gets called by non-generic code.
For your assignment it looks like you're supposed to write a generic method linearSearch with a bounded type parameter <E extends Comparable<E>> (essentially meaning that whatever array type you pass to linearSearch, it has to be some subtype of Comparable). Then you're probably supposed to pass it different types of arrays in main, like Integer[], String[], etc. Your main method won't be generic. You'll just have an if...else if chain for each type that args[0] requires.
The below code doesn't work :
class Stats<T extends Number>
{
T[] nums;
Stats(T[] o)
{
nums = o;
}
double average()
{
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
boolean sameAvg(Stats<T> ob)
{
if(average() == ob.average())
return true;
return false;
}
}
class BoundsDemo
{
public static void main(String args[])
{
Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new Stats<Integer>(inums);
double v = iob.average();
System.out.println("iob average is " + v);
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new Stats<Double>(dnums);
double w = dob.average();
System.out.println("dob average is " + w);
if(iob.sameAvg(dob))
System.out.println("Averages are the same.");
else
System.out.println("Averages differ.");
}
}
But, the following works:
class Gen04<T extends Number>
{
T num;
void set(T a)
{
num = a;
}
T get()
{
return num;
}
boolean equals(Gen04<T> ob)
{
if(num.doubleValue() == ob.get().doubleValue())
return true;
else
return false;
}
}
class GenericCompare
{
public static void main(String args[])
{
Gen04<Integer> obI1 = new Gen04<Integer>();
obI1.set(new Integer(5));
Gen04<Double> obI2 = new Gen04<Double>();
obI2.set(new Double(5.0));
System.out.println("obI1 and obI2 are equal = "+obI1.equals(obI2));
}
}
What is the difference ? In both the programs, I am trying to call a method with parameter type different from the parameter type of the invoking object
In the first snippet, in iob.sameAvg(dob) you attempt to pass a Stats<Double> instance to a method of Stats<Integer>, which expects a Stats<Integer> argument. Since Stats<Double> is not a sub-class of Stats<Integer>, the compiler doesn't accept it.
In the second snippet you call obI1.equals(obI2). equals is a method of Object class that accepts an Object argument, so you can pass any Object instance to it.
EDIT : I see were you got confused. Your second snippet has an equals(Gen04<T> ob) method that overloads Object's equals, but that method is not being called. Instead, the compiler chooses the equals(Object other) method of Object class. If you rename your equals method and call the renamed method from your main, the second snippet would produce the same error.
I have some code to figure out the correctness of manipulations on different data types (int, long, double) comparing to BigInteger. The manipulation is getting a factorial of a number until the result is the same as BigInteger has.
The question is how could i change my code, make more generic, compact and clean? How could i get the only one method, not 4 for different types?
The logic in this methods is pretty the same as well as the flow.
The code (without comparing logic) is:
private static HashMap<BigInteger, BigInteger> bigIntegerFactorials = new HashMap<>();
private static BigInteger bigIntegerFactorial(BigInteger number) {
if (number.equals(BigInteger.ONE)) {
return BigInteger.ONE;
}
BigInteger result = bigIntegerFactorials.get(number);
if (result == null) {
result = number.multiply(bigIntegerFactorial(number.subtract(BigInteger.ONE)));
bigIntegerFactorials.put(number, result);
}
return result;
}
private static HashMap<Integer, Integer> intFactorials = new HashMap<>();
private static int intFactorial(int number) {
if (number == 1) {
return 1;
}
Integer result = intFactorials.get(number);
if (result == null) {
result = number * intFactorial(number - 1);
intFactorials.put(number, result);
}
return result;
}
private static HashMap<Long, Long> longFactorials = new HashMap<>();
private static long longFactorial(long number) {
if (number == 1) {
return 1L;
}
Long result = longFactorials.get(number);
if (result == null) {
result = number * longFactorial(number - 1);
longFactorials.put(number, result);
}
return result;
}
private static HashMap<Double, Double> doubleFactorials = new HashMap<>();
private static double doubleFactorial(double number) {
if (number == 1) {
return 1.;
}
Double result = doubleFactorials.get(number);
if (result == null) {
result = number * doubleFactorial(number - 1);
doubleFactorials.put(number, result);
}
return result;
}
Thanks a lot in advance.
You could pass the multiply and decrement functions to a generic method:
private static Map<Number, Number> factorials = new HashMap<> ();
private static <T extends Number> T factorial(T n, BinaryOperator<T> multiply, UnaryOperator<T> decrement) {
if (n.doubleValue() == 1) return n;
T result = (T) factorials.get(n);
if (result == null ){
result = multiply.apply(n, factorial(decrement.apply(n), multiply, decrement));
factorials.put(n, result);
}
return result;
}
Then you can change your primitive methods like so:
public static int intFactorial(int number) {
return factorial(number, (i, j) -> i * j, i -> i - 1);
}
WARNING: this method seems to crash Netbeans but compiles fine with javac...
If you are really trying to compute a factorial, then there isn't any need for double or BigDouble since factorial only applies to integer values. And since that's true, you can convert any integer type to BigInteger and go with a single method that takes any Number returns BigInteger.
Here's a test class
public class Junk {
public static void main(String[] args) {
long val = 9;
Junk j = new Junk();
System.out.println(val + "! = " + j.factorial(val));
BigInteger nine = new BigInteger("9");
System.out.println(nine + "! = " + j.factorial(nine));
short nine_short = 9;
System.out.println(nine_short + "! = " + j.factorial(nine_short));
}
private HashMap<BigInteger, BigInteger> map = new HashMap<>();
public BigInteger factorial(Number number){
if(1 == number.intValue()){
return BigInteger.ONE;
}
BigInteger bigInteger = new BigInteger(number.toString());
BigInteger result = map.get(bigInteger);
if(result == null){
result = bigInteger.multiply(factorial(bigInteger.subtract(BigInteger.ONE)));
map.put(bigInteger,result);
}
return result;
}
}
And the output
9! = 362880
9! = 362880
9! = 362880