I need to call a function with the following signature.
createColumn (N name, V value, Serializer<N> nameSerializer, Serializer<V> valueSerializer)
I want to pass variables of type Object which might have been assigned values of integer or string, I want the type casting to be performed automatically..according to the values that I assigned to Object type variables instead of explicit cast like this:-
Object object1= "MY_AGE";
// string value assigned to to object type variable
Object object2= 31; // integer value assigned to object type variable
createColumn ((String)object1, (int)object2, ....); // Since the datatype of object1 & object2 would not be same everytime while I am calling this function in a for loop, I want that it should automatically cast according to the value I assign to it.* So I am seeking something like this, if possible:-
createColumn (object1, object2, ....);
You can call the following since you don't want to check at compile time that the types match,
createColumn(object1, object2, (Serializer)serializer1, (Serializer)serializer2);
EDIT: This compiles for me (with an "Unchecked" warning)
interface Serializer<T> { }
public static <N,V> void createColumn (N name, V value, Serializer<N> nameSerializer, Serializer<V> valueSerializer) {
}
public static void main(String[] args) throws NoSuchFieldException {
Object object1 = "hi";
Object object2 = 31;
Serializer<String> serializer1 = null;
Serializer<Integer> serializer2 = null;
createColumn(object1, object2, (Serializer) serializer1, (Serializer) serializer2);
}
As I understand your question is not about casting (which deals with compile-time declared types), but conversion (which deals with runtime type of objects).
Consider using String.valueOf() method for your Object arguments. For both Integer and String it will produce their String representation.
I don't think it is possible, you have given the variable a type object and i'm not aware of any way to determine if it is really an int or string unless you use some ugly logic to see what characters the value consists of but that isn't going to be fool proof unless the value will always be either an integer or a string.
Do you need to pass integers or can everything just be passed as a string?
Related
I have added three methods with parameters:
public static void doSomething(Object obj) {
System.out.println("Object called");
}
public static void doSomething(char[] obj) {
System.out.println("Array called");
}
public static void doSomething(Integer obj) {
System.out.println("Integer called");
}
When I am calling doSomething(null) , then compiler throws error as ambiguous methods. So is the issue because Integer and char[] methods or Integer and Object methods?
Java will always try to use the most specific applicable version of a method that's available (see JLS ยง15.12.2).
Object, char[] and Integer can all take null as a valid value. Therefore all 3 version are applicable, so Java will have to find the most specific one.
Since Object is the super-type of char[], the array version is more specific than the Object-version. So if only those two methods exist, the char[] version will be chosen.
When both the char[] and Integer versions are available, then both of them are more specific than Object but none is more specific than the other, so Java can't decide which one to call. In this case you'll have to explicitly mention which one you want to call by casting the argument to the appropriate type.
Note that in practice this problem occurs far more seldom than one might think. The reason for this is that it only happens when you're explicitly calling a method with null or with a variable of a rather un-specific type (such as Object).
On the contrary, the following invocation would be perfectly unambiguous:
char[] x = null;
doSomething(x);
Although you're still passing the value null, Java knows exactly which method to call, since it will take the type of the variable into account.
Each pair of these three methods is ambiguous by itself when called with a null argument. Because each parameter type is a reference type.
The following are the three ways to call one specific method of yours with null.
doSomething( (Object) null);
doSomething( (Integer) null);
doSomething( (char[]) null);
May I suggest to remove this ambiguity if you actually plan to call these methods with null arguments. Such a design invites errors in the future.
null is a valid value for any of the three types; so the compiler cannot decide which function to use. Use something like doSomething((Object)null) or doSomething((Integer)null) instead.
Every class in Java extends Object class.Even Integer class also extends Object. Hence both Object and Integer are considered as Object instance. So when you pass null as a parameter than compiler gets confused that which object method to call i.e. With parameter Object or parameter Integer since they both are object and their reference can be null. But the primitives in java does not extends Object.
I Have tried this and when there is exactly one pair of overloaded method and one of them has a parameter type Object then the compiler will always select the method with more specific type. But when there is more than one specific type, then the compiler throws an ambiguous method error.
Since this is a compile time event, this can only happen when one intentionally passes null to this method. If this is done intentionally then it is better to overload this method again with no parameter or create another method altogether.
class Sample{
public static void main (String[] args) {
Sample s = new Sample();
s.printVal(null);
}
public static void printVal(Object i){
System.out.println("obj called "+i);
}
public static void printVal(Integer i){
System.out.println("Int called "+i);
}
}
The output is Int called null and so ambiguity is with char[] and Integer
there is an ambiguity because of doSomething(char[] obj) and doSomething(Integer obj).
char[] and Integer both are the same superior for null that's why they are ambiguous.
I started with java a couple of weeks ago. Before that i had multiple years working with c/c++ on embedded targets and with c# with UI Stuff on Win PCs.
I got this very simple example:
public class StreamProcessing {
public static void main(String[] args) {
Stream stream = new Stream(); //after this line: Stream string empty
StreamFiller.fillStream(stream); //after this line: Stream string not empty any more
StreamPrinter.printStream(stream);
}
}
I'd expect that whatever StreamFiller.fillStream() does, the argument is copied. However it looks like fillStream is modifying the actual stream object itself.
The Stream class basically contains a string
public class Stream {
private String content = "";
int index = 0;
public char readChar() {
if (index < content.length()) {
return content.charAt(index++);
} else {
return 0;
}
}
public void writeString(String str) {
content += str;
}
}
The Streamfiller should modify it's stream copy but not the original reference
public class StreamFiller {
public static void fillStream( Stream stream ) {
stream.writeString( "This is a" );
stream.writeString( " stream." );
}
}
Please correct me if I'm wrong, but since the actual text of the string class is allocated on the heap, both the StreamProcessing () Stream object and the (supposed copied) local object of fillStream() point to the same address on the heap (yeah i now it's not an actual memory address like in c/c++ but some unique object identifier)
So is my assumption correct? Non trivial objects (aka objects allocated on the heap) are passed by reference?
thx for your help :)
The Java language does not let you make heap / stack distinction in your code the way C and C++ do.
Instead, it divides all data types in to two groups:
Primitive types:
These are simple built in numerical types such as int, double or boolean (not a numerical type in Java).
Note that String is not such a type!
Object types:
If it is a class, it is an object type. This goes for built in types such as String and for user defined types such as your Stream class.
For these types, all you ever see is a reference, whether you are looking at a local variable, class member, instance member, or function parameter.
Lets look at a simple example:
public class A {
public int a;
public static void main(String [] args) {
A var1 = new A();
A var2 = var1;
var1.a = 42;
System.out.println("var2.a = " + var2.a);
}
}
If you compile and run this example it will print 42.
In C++ the line A var2 = var1; would have invoked a copy constructor and created a new object but in Java there is no such thing. If you want a copy, you need to invoke clone method explicitly.
What is held in var1 and copied to var2 is just a reference.
So both vars "point" to the same object.
And again - it does not matter if the class is trivial or not. Even if a class is completely empty, you will still only be given and work with a reference to any object of this class.
As for the primitive types mentioned earlier, Java has wrapper classes such as Integer and Boolean for them.
You might want to read about "boxing" and "unboxing".
One more thing to note is that some types are immutable - that is, they do not provide a way to change their data once created.
String in Java is an immutable type, but it is also a bit different from any other type.
It has special privileges.
While Java does not support operator overloading like C++ does, for String type the language does provide a special + operator that preforms string concatenation.
How ever, since String objects are immutable, any concatenation operation will create a brand new String object, even one like this:
String a = "Hello";
a = a + " world!";
This creates a new string "Hello world" and stores the reference to it in a, leaving the reference to old "Hello" string to be garbage collected at some future point.
Even though in Java everything is passed by value, there is a difference between how primitive data types (such as int, char and boolean) and how reference data types are passed to a method.
When passing the value of a primitive data type, this value can only be changed in the scope of the particular method. When passing the value of a reference data type, the reference will remain the same but the value will change globally (or in whatever scope the object was initialised).
See also this for more information: https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html
I would like to write a generic method that accepts an array and something else. Each of which could be whatever type, but they must be the same. I tried this, but I could still input anything into the method.
public static <T> boolean arrayContains(T[] array, T object){
return Arrays.asList(array).contains(object);
}
I can call this method with arrayContains(new String[]{"stuff"}, new Pig()) but I only want it to accept arrayContains(new String[]{"stuff"}, "more stuff")
What you are trying to do is tricky because any array (except an array of primitives) is an Object[], so as you have noticed, the method will always accept any array and any object.
One way around this could be to pass an explicit Class object, like this
public static <T> boolean arrayContains(T[] array, T object, Class<T> clazz)
Then you could write
arrayContains(new String[]{"stuff"}, "more stuff", String.class)
but not
arrayContains(new String[]{"stuff"}, new Pig(), String.class)
You can't.
Or put another way, any array and any reference already satisfy your requirement, "array of type x and another varable with the same type as the array", because any array is an "array of type Object", and any reference is of the same type (Object).
What you want serves no type-safety purpose. Consider, hypothetically, that there were a language feature to do what you want. It can only operate on the compile-time types of the expressions of the arguments. But someone can always do this:
Object[] foo = anyArrayExpression;
Object bar = anyReferenceExpression;
arrayContains(foo, bar);
or
arrayContains((Object[])anyArrayExpression, (Object)anyReferenceExpression);
(and, by the way, neither of these are doing anything fishy or unsafe. An upcast is always 100% safe and legit operation in Java.)
So any array and any reference can always be passed to your function anyway, and your function always needs to handle the actual objects being any type of array and any type object anyway. So your feature achieves nothing.
Even if you restrict it to be subtypes of some type X, and your function only takes X[] and X, it can still always be the case that the actual runtime class of the objects pointed to by the references passed in are Y[] and Z, where Y and Z are unrelated subtypes of X. This is just a fact of how the Java type system works. So your function will always have to deal with the runtime component type of the array being potentially unrelated to the runtime class of the other object, no matter how you do it. (The only exception would be if X were final, so it has no subclasses, but then your restriction would be meaningless because it would be trivially always true.)
What about next?
public static <A, E extends A> boolean arrayContains(A[] array, E object){
return Arrays.asList(array).contains(object);
}
In this case the type of the second argument must extend (or be the same as) the type of the array's elements.
This is what I ended up doing.
public static boolean arrayContains(Object[] array, Object object) {
if (array.getClass().getComponentType() != object.getClass()) {
throw new IllegalArgumentException("Type of array and object are not equal! " + array.getClass().getComponentType().getName() + " != " + object.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
}
return Arrays.asList(array).contains(object);
}
You need to provide multiple parameterized types, and then do something if they don't match.
public static <T, S> boolean arrayContains(T[] array, S object){
System.out.println("array.class: " + array.getClass().getComponentType().getName());
System.out.println("object.class: " + object.getClass().getName());
System.out.println("array.class == object.class: " + (array.getClass().getComponentType() == object.getClass()));
// TODO: do something if the types don't match
return Arrays.asList(array).contains(object);
}
Previous question
I have the following code:
ArrayList<Object> list = new ArrayList<Object>();
list.add("StringType");
list.add(5);
list.add(new RandomClass());
List<Class<?>> classes = new ArrayList<>();
classes.add(String.class);
classes.add(int.class);
classes.add(RandomClass.class);
for (int i = 0; i < list.size(); i++) {
if (classes.get(i).isInstance(list.get(i))) {
...
}
}
if (isvalid)
mymethod(...);
public void mymethod(String string, int num, RandomClass randomClass){ }
Now I'm trying to cast the object into the right type with a method using a string argument.
Instead of:
mymethod( (String) list.get(0), (int) list.get(1), (RandomClass) list.get(2) );
I would like to reuse the definition created above for the cast.
mymethod( ( define.get(0) ) list.get(0), .... );
I've also tried using the Class.cast(obj) but of course it returns a type '?' which again defeats the purpose of casting it again using (String).
What is type safety?
In computer science, type safety is the extent to which a programming
language discourages or prevents type errors.
If code is type safe, then the compiler can verify, at compile time, that all the types are correct:
String getName() {
return "name";
}
The compiler knows that "name" must be a String so it can verify that this code will never throw a type error.
Once you do something like:
int getNumber() {
(int) number;
}
The need to explicitly cast to int tells you that this code has an error condition, namely when number is not of type int or a type that is assignable to int.
How does it affect you?
Your code:
define.get(0).cast(list.get(0))
You want the return type of this statement to be of the type of get(0). But the compiler has no way of knowing, at compile time, what define.get(0) returns. This is inidcated to you by the return type.
You have a List<Class<?>>, i.e. a List of a class of "I don't care what type". You then use a member of this List to cast a member of your other List - the only result can be an "I don't care what type".
You can hack around this with:
<T> T get(final int i) {
return (T) define.get(i).cast(list.get(i));
}
This will happily compile:
final String thing = get(0);
As will:
final int thing = get(0);
i.e. all that you have done is to endogenise the cast. The error condition still exists.
define.get(0).cast(list.get(0)) would attempt to cast list.get(0) to the required type.
In order to be able to select the appropriate method, the compiler needs to know at compile time what the types of the arguments are. Or at least a general category such as List<?> etc.
This is needed to support overloading of methods. There can be many methods with the same name, but with different parameter types.
Since you are asking the VM to call a method when it can't determine which exact method you want to call, because it doesn't know at compile time what the types of your parameters are, what you ask cannot be done in Java.
Here is the relevant section from the Java Language Specification.
What it says is that the system selects at compile time which method signature to use, and then, at run time, the particular implementation of that method signature that's correct for the given instance.
You don't actually need to store object's class separately
list.get(0).getClass()
will get you the class of the stored object and then you can use what #Eran suggested
and
list.get(0).getClass().getName()
will get you the String name of your class
For example i have a string input "int",can i declare a variable base on that input?
(Not switch check please). I mean something like this (pseudo-code) or similar:
String str="int";
new (variable_name,"int");
// create new variable with int datatype.
You can do this:
String className = "MyClass";
Object obj = Class.forName(className).newInstance();
But it won't work for primitive types.
If instead of using primitive types you will use cannonical name of Object based class you can try to do this
public Object loadClass(String className) {
return Class.forName(className).newInstance(); //this throw some exceptions.
}
Not practically, Java is strongly typed and the type of all variables must be known at compile time if you are to do anything useful with them.
For example, you could do something like this;
String str = "java.lang.Integer";
Class clazz = Class.forName(str);
Object o = clazz.newInstance();
..which will give you an Object o whose type is determined at runtime by the value of the String str. You can't do anything useful with it though without first casting it to the actual type, which must be known at compile time.