I am a bit rusty and got stuck on sort of elementary problem. I want my method printBinary to take in a Long or Integer. and base on the input type I want to call the respective Object's toBinaryString() method.
Now I know there are alternative ways like method overload, I can probably create two methods both call printBinary but one takes Long and the other takes Integer. But if I want to do this in one method how can I achieve that?
public static <T extends Object> void print(T arg){
System.out.println(arg);
}
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
Explanation
Your write
public static <T extends Number> void printBinary(T arg)
so you declared T to be something which extends Number. Very important, note that nothing more is known about T. So you can only safely assume it is Number but nothing more (without checking, for example using instanceof).
The next thing is that you write
// requires long
print(Long.toBinaryString(arg));
// requires int
print(Integer.toBinaryString(arg));
but both methods don't require Number, they require long and int. Take a look at their documentation (Long, Integer).
Even if we consider boxing, Java would only be able to convert Long into long and Integer into int (or vice versa). But not Number to Long or Number to Integer. A Number is not necessarily a Long or Integer. It could, for example, also be a Double. And you don't know what T is, you only know it is at least of type Number.
Utility method
So what you will need to do is explicitly convert the arg to long and int. Therefore you can use utility methods provided by Number, namely Number#longValue and Number#intValue:
print(Long.toBinaryString(arg.longValue()));
print(Integer.toBinaryString(arg.intValue()));
Cast after instanceof
The other possibly is to cast them. As you have checked with instanceof that T truly is of type Long or Integer you can safely cast it:
print(Long.toBinaryString((Long) arg));
print(Integer.toBinaryString((Integer) arg));
After the cast Java will automatically convert Long to long and Integer to int, this is called unboxing. Or to make it more explicit, call the unboxing method by yourself:
print(Long.toBinaryString(((Long) arg).longValue()));
print(Integer.toBinaryString(((Integer) arg).intValue()));
Notes
To be precise the cast to Integer is unchecked with your current code. You need to also check arg instanceof Integer, safer would thus be:
if (arg instanceof Long) {
// Cast to Long is safe
} else if (arg instanceof Integer) {
// Cast to Integer is safe
} else {
// Neither Long nor Integer, for example Double
throw new IllegalArgumentException("Only Long and Integer supported.");
}
If you want your method
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
to work, you need put casts when calling toBinaryString(art) method, for example like this
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString((Long) arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString((Integer) arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
But still, there is one problem with the code that you posted. As it was said by #Jacob H, declaration like <T extends Number> means that method will accept parameters of any class that extends the Number class. So it will work if parameter will be of type Integer or Long, but will fail when for example Float value will be passed.
Unfortunately toBinaryString method is not implemented by Number class, but is a method provided by subclasses like Integer and Long.
So for your case, it would be probably better to implement two separated methods that take parameters of different types - Long and Integer.
In case you really want to use this generic notation, but not handle input of other types than Integer and Long, you will probably need to itroduce some kind of parameter type validation. But still, definition of method that takes Number type parameter and does not handle some of it's subclasses and some does might be confusing and will look like a bad design.
You just want to use <Number>, <T extends Number> means "some type T that extends Number", so it expects either Long or Integer, but not both. <Number> means "Number, or any type extending Number"
The best solution is to use method overload. Using Number, the method will also accept doubles.
If you really want to use Number, you don't need generics. Rewrite the methods like this
public static void print(Object arg)
public static void printBinary(Number arg)
Related
I am writing a function which uses generic parameters and converts them to their specific datatype adds them and returns the added value. I have done this for integer and double, but can't do the same for the string
I have tried using toString() function as well as String constructor but nothing seems to work!
public static <T extends Number> T add(T a, T b) {
if (a instanceof Integer) {
return (T) Integer.valueOf(a.intValue() + b.intValue());
} else if (a instanceof Double) {
return (T) Double.valueOf(b.doubleValue() + b.doubleValue());
} else if (a instanceof String) {
return (T) String.valueOf(a.toString() + b.toString());
}
}
I expect that it should return generic type argument but it throws couple of compile time errors:
java: incompatible types: T cannot be converted to java.lang.String
and
ava: incompatible types: java.lang.String cannot be converted to T
Your Generic type T is declared to extend Number. String does not extend Number. And this is the root of your problem. Either your incoming parameters a and b extend number and thus may not be of type String or your T generic type may not extend Number. Another solution is to have your method as it is and not deal with String type there and add another method that receives 2 String params. However, note that adding 2 strings just concatenates them, so "2" + "4" will give you "24" which is probably not what you want/expect. If you expect Strings that represent numeric values you will need to write your own code to parse your String params to Integer or Long or other implementation of Number interface and then add them up
Probably I am missing something and maybe my assumptions were wrong, but I thought that when I declare parametrized method with type T then no matter how much variables there are with that type it is still the same type. But I see that this compiles and it oposses my view.
static <T> void f(T a, T b) { }
public static void main(String[] args) {
f(Integer.MIN_VALUE, "...");
}
So if my method is parametrized with one type and I am using that one type in two paramteres why does it allow me to send two objects with two totally different types? I guess it comes down to treating T as Object?
Although Integer and String are two different types, they still share a common super-type. Serializable.
To verify this, lets return T,
static <T> T f(T a, T b) {
return null;
}
Serializable s = f(Integer.MIN_VALUE, "..."); // compiles
The compiler will resolve (or infer, not sure about the technical term) to the most specific type. For example,
Number number = f(Integer.MAX_VALUE, BigDecimal.ONE);
Now, the type resolved is Number because both types are subtypes of Number,as well as Serializable, as well as Object of course.
I was reading a java book, and came up with this example
public class Main {
public static void main(String[] args) {
play(4); // DOES NOT COMPILE
play(4L);
glide(1,4); // THIS ONE COMPILES
}
public static void play(Long l) { }
public static void play(Long... l) { }
public static void glide(long i, long j) {}
}
but didn't quite get the idea why doesn't java cast the int to long and call the first method, because it does the cast for the method with 2 parameters?
Because the method glide uses parameters of type long, and java easily casts int to long, but the first method uses parameter of type Long, which is the wrapper class for long type, not int, that's why the first method doesn't compile
The third method, glide, is an example of a widening cast from an int to a long which is done automatically.
The first method, play(Long l), accepts a Long (capital L) object. Primitives cannot be cast to objects which is why your first example doesn't compile, but the compiler will convert a primitive to its equivalent object via "autoboxing" which is why play (4L) works.
Note these rules:
You CANNOT widen and then box. (An int can't become a Long.)
You CANNOT widen from one wrapper type to another. (IS-A fails.)
You can box and then widen. (An int can become an Object, via Integer.)
I didn't even know this was doable, but I saw while perusing some code online a method with a signature like this:
public List<Void> read( ... )
... What? Is there ever a reason to do this? What could this List even hold? As far as I was aware, it's not possible to instantiate a Void object.
It is possible that this method signature was created as a by-product of some generic class.
For example, SwingWorker has two type parameters, one for final result and one for intermediate results. If you just don't want to use any intermediate results, you pass Void as the type parameter, resulting in some methods returning Void - i.e. nothing.
If there were a method List<V> returnAllIntermediateResults() in SwingWorker with Void as the type parameter V, it would have created a method just like you posted in your question.
The code would be perfectly valid. You can instantiate any implementation of the List interface (e.g. ArrayList) with type parameter Void. But the only value a Void type can have is null. So the list could not hold anything else but nulls, if the implementation allows null elements.
One case in which it may be useful is if you wanted to return a collection of return values from a function. Say
static List<T> forEach(Func<A,T> func, List<A> items) {
List<T> ret = new List<T>();
for(int i = 0; i< items.length; i++) {
ret.add(func.call(items[i]);
}
return ret;
}
public static void main() {
...
List<Void> boringResult =
forEach(
new Func<Void, Integer> {#override Void call(Integer i) {...}});
}
Not that useful but you could see a case where it was required.
List<Void> is weird. It can only have null elements, since you can't create an object of type Void. I don't think there is a practical use for such a thing.
Void is part of java.lang. It's not a special keyword or anything. It's a "pseudo-type" (according to the docs) used to as a place-holder to represent the Class object corresponding to void, as in Class<Void>. From the docs for Class:
The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
The Void class exists mainly for the sake of the last part of this, so you can write:
Class<Void> voidType = void.class; // == Void.TYPE
just like you can write:
Class<Integer> intType = int.class; // == Integer.TYPE
I agree, it's odd.
I can see a use for it if you want to extend a generic class and return void from a method. I've bumped into a case were I want to use int and had to use Integer because java generics don't like primitive types.
public interface ObjectUserPool<E, T> {
public E useObject(T o);
}
public class NonReturningObjectUserPool extends ObjectUserPool<Void, Integer> {
public Void useObject(Integer i);
}
I think this is what the java API is saying, though to be honest I can't really find a use for NonReturningObjectUserPool.
for example, i have this class:
public class Col {
static void test(int a)
{
System.out.println("int");
}
public static void main(String args[])
{
Col.test(12); //1
Col.test((byte)12); //2
Col.test((long)100); //3
}
}
and now me intresting how algoritm work this code.
I think, that this steps:
1 line - all correct call method with int param, perfect.
2 line - call method with byte param...oooops. what do? Java try widening byte to int? Its true?
3 line call method with long param... again ooops. what do? convert long to int java can't, because loss of accuracy. its try? And in result - Exception.
Than I add this:
public static void test(Object a)
{
System.out.println("Object");
}
and if a call:
Col.test((long)100);
all correct, no Exception
so, what the relation between primitive type long and Object?
Yes, there's an implicit conversion from byte to int, but no implicit conversion from long to int (because of the likelihood of losing information).
In the third case, you're using autoboxing which will convert a long (primitive type) to a Long (class type).
You can see that by changing the body of test to:
public static void test(Object a)
{
System.out.println(a.getClass());
}
It will then print out class java.lang.Long.
Your first example shows conversion of primitive types. The second shows boxing and unboxing, which is - in brief - a convenient conversion between primitive type (like long) and their wrapper classes (java.lang.Long in this case).
Overloading is implementing methods that have the same name but different parameters. Here we have two methods
static void test(int a){}
static void test(Object a){}
and call it with test((long) 100). The first method can't be called, because the JVM won't narrow a long to an int without explicit casting. But the JVM (Version 1.5+) can convert the long value to a Long (autoboxing) and test(Long.valueOf((long) 100)) is a good match for the second method.
This is because auto-boxing feature.
Actually you have promoted the primitive to long and while calling test method automatically it is seaching for its equivalent type hence it is calling test(Object a).You can see like this Col.test(new Integer(12));this will also call test(Object a).Also you can refer this link
Determining if an Object is of primitive type
public static void test(Object obj) {
if (obj instanceof Integer) {
System.out.println("Integer");
} else if (obj instanceof Double) {
System.out.println("Double");
} else if (obj instanceof Float) {
System.out.println("Float");
} else if (obj instanceof Long) {
System.out.println("Long");
}
}
All java primitives have corresponding boxed "types" that are actual classes. In you example, long has a corresponding class Long. This class extends from Object.
What you have experienced is boxing and unboxing.
It is a feature introduced in Java 5. Its called Autoboxing. In this a primitive type is converted to Object (in your case long to Long). See this link for details on Autoboxing.