Java: which is faster overloading or if/else - java

I have child classes, each carries a different type of value along with other members. There may be a LongObject, IntObject, StringObject, etc.
I will be given a value, which can be a long, int, string, etc., and I have to create a LongObject, IntObject, StringObject, etc., respectively.
Would it be faster to overload a method as shown below (a) or just use a elseif as shown below (b)?
It may not be a noticeable performance difference. It may be that the overloaded methods are implemented in a similar manner to the if/else anyway. I don't know.
I can also hear some of you saying to just test it. Sure, I ought to. I would also like to know how this type of overloading is handled under the hood, if anyone knows.
Please let me know what you think.
Thanks,
jbu
a)
BaseObject getObject(long l)
{
return new LongObject(l);
}
BaseObject getObject(int i)
{
return new IntObject(i);
}
BaseObject getObject(String s)
{
return new StringObject(s);
}
...
b)
BaseObject getObject(Object x)
{
if(value is a long)
return new LongObject((Long)x);
else if(value is an int)
return new IntObject((Int)x);
else if(value is a String)
return new StringObject((String)x);
...
}
edit: I guess I didn't completely add all the details, some of you caught it. For both choices, I still have to get an object/value and from the value determine what type it is. Therefore, I still have to do an if/else of some sort to even use the overloaded methods.

There's a massive discrepancy here: overloads are chosen at compile-time whereas your "if (value is a long)" would be an execution-time test.
If you know the type at compile-time, I strongly suggest you use that information. If you don't, then the overloading option isn't really feasible anyway.
EDIT: The comment suggests I elaborate a bit about overloads being chosen at compile-time.
The compiler picks which method signature is called based on compile-time information about the arguments. This is unlike overriding where the method implementation to use is determined by the type of the actual target of the method.
Here's an example:
public class Test
{
public static void main(String[] args)
{
Object x = "I'm a string";
foo(x);
}
public static void foo(String x)
{
System.out.println("foo(String)");
}
public static void foo(Object x)
{
System.out.println("foo(Object)");
}
}
This prints foo(Object) because the compile-time type of x is Object, not String. The fact that the execution-time type of the object that x refers to is String doesn't mean that foo(String) is called.

The overloading solution is much faster (and better) because it is resolved at compile time.
Basically the compiler figure out which method to invoke when you pass a value to it. When you call getObject("abc"), the compiler will emit calls to method:
BaseObject getObject(String s)
{
return new StringObject(s);
}
rather than trying to go through your if ... else state and evaluate the object type (which is a slow activity) at run time.

Dont worry about, unless the condition is tested millions of times a second it doesnt matter.

a) (overloading) will be faster as it gives the just in time compiler a chance to optimize. It could decide to inline the new object creation.

Any time you have to evaluate type information at run-time, it's liable to be a relatively slow operation. When you have a choice, it's nearly always preferable to write code that does so at compile time.
This is often referred to as "early binding" versus "late binding."

Cascading conditionals is bad karma in OO programming, and really ugly when testing the type of an object. The language already provides that king of test, with polymorphism.
Also, as a non-java guy, I'd use getLong(l), getInt(i), getString(s).
I find method overloading more confusing than not (imho static types shouldn't influence the program execution (other than optimization-wise (and despite appearances I'm not a Lisper :) )))

Most importantly, getObj(int), getObj(string), etc. - will fail at compile time, if you try to pass something not expected.
If you allow any object to be passed into the method, you might have an instance where the app tries to pass in something that the method can't deal with, and you wind up with an ugly runtime error.

Related

Passing Methods in Java

I'm trying to accomplish passing a method in Java.
Here is the birds-eye-view of what I'm trying to do as a dummy example:
public final class A {
private String value;
public A(String value) {
this.value = value;
}
public final Object bind(Function<String, String> func) {
this.value = func.apply(value);
return this;
}
// Rest of the logic here to deal with `value`
}
public final class B {
public static void main(String[] args) {
A<T> a = new A("hello");
a.bind(B::methodOne).bind(B::methodTwo);
}
private String methodOne(String s) {
// method logic here
return "haha";
}
private String methodTwo(String s) {
// method logic here
return "hi";
}
}
So, basically, I've methods in a class, B in the above example, and I want to pass the methods of B to A and store the return value of that method of B on A for further processing.
I've tried to make use of method reference feature of Java but since I don't code daily with Java, I'm having a hard time getting my head around how to properly accomplish this while fulfilling the constraints above.
Currently, I'm getting a incompatible types: invalid method reference error while I do the binding in the main method.
Update
Made changes on my constraints of the program.
EDIT: The asker updated their question a lot after reading this answer. Crucially, originally each method had a completely different signature (different param types and amounts, and different return types). I'm leaving this answer untouched, be aware it is no longer particularly relevant to the question as it currently stands.
This doesn't work well because the methods you want to pass have completely different signatures (methodOne's is (B, String) -> int (why do you have a capital I Int in there, is that a typo?), methodTwo is (B) -> String, and methodThree is (B, String, String) -> String.
In java lambdas must fit a functional interface. It is not possible to have a functional interface for a variable number of input arguments.
With generics you can attempt to paper over the fact that the types of your inputs and output are different every time.
This really sounds like an X/Y problem: You have problem X (which you didn't explain and we don't know what it is), and you thought: I know! I'll use lambdas to abstract away the notion of 'a method'... and now you're asking questions about that.
But you're asking the wrong question. Ask X. Because even if hypothetically you could somehow fit your 3 methods all in the same lambda type (you can't), you would not then be able to invoke them.
Here's one more workable notion, but I have no idea if it solves your X because you didn't explain this:
#FunctionalInterface
public interface MyXThinger {
Object result(Object... args);
}
If you want to invoke this, how would you know that the particular MyXThinger (you didn't explain what X is, so I can't come up with a good name here) works if you pass 2 strings, and crashes if you pass anything else? That's.. more or less why I find your problem description insufficient to give solid advice here.
You're writing the expression B::methodOne in a static context.
non-static methods have an invisible parameter, called 'the receiver': It's the instance.
So, in a static context, B::methodOne has the signature: (B, String) -> String. What your bind method wants is (String) -> String, and these two are not compatible. Therefore, this does not work.
There are two ways to fix it:
create an instance: B b = new B(); a.bind(b::methodOne);. The expression b::methodOne, where b is a variable of type B referencing an actual instance of B (as created with new B()) DOES have the signature (String) -> String as required.
Make the methodOne method static, at which point it no longer has the invisible B instance parameter.
Your code is also littered with rookie mistakes; you must call a.bind and not A.bind, using A and B as class names is extremely confusing, your bind method returns Object (it should return A), etc. The way to fix those is to learn basic java, I think; trying to tackle those mistakes bit by bit seems unsuitable for what stackoverflow is for. Thus, I leave those as an exercise for you.

Cast to instance's type automatically

I have a function that should take an instance of anything extending Object, and just pass the casted instance to a function. I don't want to use a switch, as the function can accept a huge number of object types, so it would become a very big method.
public void attachBufferData(ContextConstant bufferType, Object<T> data, ContextConstant usage) {
glBufferData(bufferType.getGLType(), (T) data, usage.getGLType());
}
The code above doesn't work (as Object isn't a generic type), but it should get across what I'm trying to do.
----- EDIT -----
Ok, I tried this:
public void attachBufferData(ContextConstant bufferType, Object data, Class<?> dataType, ContextConstant usage) {
glBufferData(bufferType.getGLType(), dataType.cast(data), usage.getGLType());
}
But I get a compile error glBufferData(int, long, int) is not applicable for arguments (int, capture#1-of ?, int). I guess it's a massive switch statement then :(
You can't do it like this, I'm afraid. There are three things to consider. I think (2) is the one you really want, but I'm not absolutely certain, so I've left all three issues in there for you to think about.
What signature does glBufferData() have (if it's not overloaded)? If its second parameter is of type Object, then whatever you pass will end up being viewed as an Object there, even if it's a subclass, so you wouldn't achieve anything by having it cast. You might as well just have the type of data as the same type as the second parameter to glBufferData().
If glBufferData() is an overloaded method, and you want to be calling the right one, then you can't do it dynamically: you need some code to test the real type of the class at runtime, and then you choose the right version to call. Choice of overloaded method gets resolved at compile time, not runtime, so you can't just pass it a specific instance you don't know about at compile time and then have it select the right version.
If glBufferData() is a non-overloaded method you've written, contained within your class, then you do have another and better option, which is to make your class generic. If your class takes a type parameter of T, then you can have T data as the second parameter of attachBufferData(), and you can have T data as the second parameter of glBufferData(), so that the types match up.
The point about method overloading is that it's not nearly as clever as it looks. As far as the compiler is concerned, there's really no difference between these two cases.
Case 1:
public void f(int x);
public void f(String s);
Case 2:
public void f(int x);
public void g(String s);
Although we think of case 1 as having just one overloaded method, and case 2 as having two separate methods, as far as the compiler's concerned, in each case there are two distinct methods, and they're distinct because they have distinct signatures (ignoring return type). In both cases, the compiler can choose the right method to call based on the code you write, because it can look at the type of the arguments and the name of the method you've asked for, and find one that matches. The fact that two have the same name is of no more significance than having two methods with different names but the same parameter types.
There's no mechanism for choosing which method to call at runtime in case 1, any more than there is in case 2.
You can declare the type at class level and reuse it wherever required, below is an example.
public class ExceptionHolder<T>
{
private List<T> errors = new ArrayList<T>();
public void addError( T t )
{
errors.add( t );
}
public void addError( List<T> t )
{
errors.addAll( t );
}
}
In the calling code
ExceptionHolder<String> exHolder = new ExceptionHolder<String>();
String can be substitued for any object as needed.

How do I make a function accept any type (not necessarily an object)?

I have a function
boolean isValid(/* what goes in here? */) {
//do stuff
}
In the function signature, what do I have to enter in the parameter list in order for the method to accept a single parameter that may be of any type (primitive or object)?
I have searched around on this site and google, but I could only find situations where the return type is unknown.
If you have the function accept the generic object type, Java will create an object version of any primitive data type (i.e. Integer for int).
Reading the question and the comments, it seems like your perception of how compiled languages work may be a bit off.
If you make your function accept only one type (e.g. String), then it will fail to compile if the caller does not pass an object that is an instance of that type. "Not compiling" means they will not even be able to run the program without fixing the error. The compiler enforces this type safety for you, so you don't have to worry about this.
After reading through the comments it seems like you have a business need to valid any object people pass to you, and possibly, you will have to support more types as time goes on.
The simplest solution is like what jtoomey said, make a method like public boolean isValid(Object val). However, think about how much if statements you have to write, and how hard is it to modify the code when new type validation are needed.
To me, I would probably do something bit more complicated than just providing a single method. I would leverage factory to create validator base on class type like:
public interface Validator<T> {
public boolean isValid(T val) {
}
}
public class ValidatorFactory {
public static ValidatorFactory create(String configFile) {
/*read config and create new instance */
}
public Validator<T> createValidator(Class<T> clazz) {
/* base on config and create validator base on type*/
}
}
public class Application {
public static ValidatorFactory vFactory = ValidatorFactory.create()
public static void main(String[] args) {
Object val = Arguments.getVal(); //assume this exists
Validator<Object> validator = vFactory.create(val.class);
if (validator == null) {
System.out.println("not valid");
return;
}
System.out.println(validator.isValid());
}
}
Note that personally I feel this is a terrible solution, because you are throwing away the type safe feature of Java. But if you really must, having a configurable factory would be better than just a method that takes in any type. Having the Validator interface allows you to know the type when you are writing validation code.
I think you are a bit confused.
I will try to clarify some things:
You probably know that the return type of a method is the value that will be pass to the part of the code that called that method(aka know as client). Every method/function in Java can only return only one type(I assume you are familiar with basic polymorphism and you know what an IS A relationships is...). Here is the first clarification, you can return only one type, but one or many objects(There are data structure types).
Arguments are the values that the caller/client of the method/function can pass into the it for processing. Arguments can have zero or many parameters, this mean that you can pass as many objects as you want.
Parameters are exactly the same as arguments it is just a terminology difference nothing else. I you want to be accurate with the terms, you can say that parameters are the brackets when you define the method and argument are those brackets when you call the method.
In either the return type or in the parameters/arguments, the 2 types of types you can pass are Objects or primitive types.
If you use something of type Object, this will allow you to return any object(Object is the super class of all classes). But primitive types are not objects, so a type Object in a signature will not allow you to pass a number, but there is one little trick...
In Java there are special types of Objects known as primitive wrappers(Integer,Double...) this objects are object representations of primitives, sometimes they are used because they have some inbuilt functions that help programmers to easily manipulate the data(That is not the main point, keep reading...),every wrapper that represents a numerical primitive type, extends a class called Number and because of one feature that Java have known as autoboxing, you can pass primitives into Wrappers automatically.
Anyway, I don't know if this is the trick you were looking for, but in any case I want just to advice you, that there is no reason at all to do what you are trying, It sounds really strange and I don't think that such thing is really needed in real life programming.
Have a look at this code :
public static void main( String[] args )
{
App app = new App();
app.method(5);
}
public void method(Number number) {
System.out.print(number);
}
Another alternative:
Another example that I can think about in order to make a parameter universal is by the use of generics. So just for ending this answer to prove my point, here a method that will allow you pass anything you want, no mater if is primitive or object:
public class App
{
public static void main( String[] args )
{
App app = new App();
app.method(5);
app.someMethod(9);
app.someMethod("Whatever");
app.someMethod(true);
}
public void method(Number number) {
System.out.println(number);
}
public <T> void someMethod(T t) {
System.out.println(t);
}
}
I hope you find this useful, but I insist that I doubt that you will never do something like this in real life.

Can I work with generic types from a calling class?

I'm currently brushing up my Java and reading up on Generics. Since they were not treated extensively in my Java class, I'm still having some trouble wrapping my mind about it, so please keep that in mind when answering.
First of all, I'm pretty sure that what I'm trying to is not possible. However, I'd like to find out where my thinking is wrong and how I should go about achieving what I want.
What I'm trying to do is manipulating an object that implements a generic interface from another class that has no knowledge about the instantiated type. Thus, I have something like the following classes:
public interface CalledInterface<E> {
public E get() { ... }
public set(E e) { ... }
}
public class Called implements CalledInterface<String> {
...
}
Now what I want to do is:
public class Caller {
protected CalledInterface<?> c;
public Caller (CalledInterface<?> arg) {
c = arg;
}
public void run(){
// I can do this:
c.set(c.get());
// But I'd want to be able to do something like:
<?> element = c.get();
c.set(element);
}
}
What is the fundamental flaw in my thinking, if there is one? And what approach should I rather be taking?
First of all, keep in mind that generics is a compile time thing not a runtime.
Now in your Caller you defined Called c. Called is defined to implement CalledInterface<String>, so automatically, Called has the following methods generated at compile time:
String get();
void set(String e); //i assume you wanted to return void
So essentially this doesn't really make sense:
<?> element = c.get();
The Caller class isn't even aware Called is using generics internally, for it, Called just deals with strings.
UPDATE
Based on your comment, since you don't want Caller to use Called directly but use CalledInterface first thing you have to do is change the type of c to that. In this case you should not use generics, because the whole point of generics is that the same class is used in different scenarios with different types (again determined at compile time), enforcing types without having repeated code.
If I understand correctly you don't want to restrict Caller to use String, so what you have to do is change CalledInterface to not use generics, and change the methods to:
Object get();
void set(Object o);
This is how we used to do things before Generics in Java 1.4. You obviously run the risk of not having type safety, so think through whether what you want really makes design sense, because it probably does not because you have to do instanceof anyway to check the type to use the Object in a useful way (i.e. to access its methods).
If on the other hand you just change the c member (and the constructor argument of Caller) to:
CalledInterface<String> c;
Your Caller will be interacting with the CalledInterface rather than the implementation and at the same time still be type safe. So you can still pass an instance of Called and set it to c.
After your edit:
// I can do this:
c.set(c.get());
No you can't. It won't compile with c being CalledInterface<?>. (Have you even tried it?)
To do this, you can use a "capture helper":
private static <T> void helper(CalledInterface<T> c) {
c.set(c.get());
}
public void run(){
helper(c);
}
Which also solves your second problem:
private static <T> void helper(CalledInterface<T> c) {
T element = c.get();
c.set(element);
}
public void run(){
helper(c);
}
There are a few minor mistakes in your code:
protected Called c;
public Caller (CalledInterface arg) {
c = arg;
}
You are not allowed to assign arg here, because the type CalledInterface is not a subtype of Called (it is the other way around)
Also you should give type information when using CalledInterface (it is allowed to leave it out, but only for legacy purposes).
Now to the part you are wondering about. For the type Called, the compiler knows get() returns a String, if you are not interested in that, you can of course always use Object as the type of element. The compiler also knows that set() takes a String as argument, so it requires you to give one. In generics is essentially the same as using Object in a case without generics (even though it isn't allowed on the location you use it, because it doesn't make sense). This means that you would be telling the compiler to forget the type on the first line (calling get()) and to unforget it on the line below.

variable argument in java

public class Demo {
public static String doit(int x,int y)
{
return"a";
}
public static String doit(int ...val)
{
return "b";
}
public static void main(String args[])
{
System.out.println(doit(4,5));
}
}
I have a doubt that why compilier is not showing any error since doit(4,5) is causing ambiguity
When I ru the code ,I get output as a ad not b why?
The Java Language Specification defines that first method ("a") should be called (rather than "b").
See http://docs.oracle.com/javase/specs/jls/se5.0/html/expressions.html#15.12.2
In order to maintain backwards compatibility with previous Java versions (before varargs was introduced), the compiler will always pick a method with the exact number of arguments, even if a varargs method also exists.
As to whether you get a warning or not, compilers are free to add additional warnings, and there may be some that do warn about this situation, I guess yours doesn't (at least not with the settings you have)
The JLS specifies the rules that are used to resolve ambiguity. The simplified version is that the compiler first attempts to match the call against the available overloads treating the varadic argument as a simple array. If that succeeds, that is it. If it fails, it tries again treating the last argument as varadic.
In this case, the first round of matching gives a definite match.
If you are interested, the JLS rules for determining what method should be used are given in Section 15.12.2. (Warning: actual JLS rules are significantly more involved than the simplified version above, and the language / notation used is very technical.)
public static String doit(int ...val)
{
return "b";
}
will be treated by compiler as
public static String doit(int[] val)
{
return "b";
}
when passing doit(2,2), 1st method will be called, as the arguments are not an array.
When passing doit(2,2,2), the arguments will converted to array and passed to 2nd method.
change the 1st method to
public static String doit(int x,int ...y)
{
return"a";
}
call doid(2,2), it will say error
doit(int, int[]) is ambigious.
Compiler always attempts to resolve the call to the most specific function call it can find which in this case is method A. This is not really a bug but if you consider it as such it's not a compiler bug, it's in the specs. You should see the crazy stuff you could get out of this once autoboxing comes into play.
The compiler tries to match the most specific alternative. It can however be argued that it should cause an ambiguity error.
From the Java Language Specification
15.12.2.5
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
This is a compromise they made when doing the varargs spec (it's hard to know which gets called). It is recommended not to overload varargs method for this reason. Quote from their site:
Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.

Categories

Resources