0

I have some work in my college, we have to implement generic code following the module that given.

So I'm making a class that only takes Integer, Long, Double, and String

I watched some youtube videos about generic and quite understood as well, but there is a problem in my code that I can't find the solution anywhere.

This is my Class:

    public Kubus(T tinggi, T panjang,T lebar ) {

        this.tinggi = tinggi;
        this.panjang = panjang;
        this.lebar = lebar;

    }

    public T getTinggi() {
        return tinggi;
    }

    public T getPanjang() {
        return panjang;
    }

    public T getLebar() {
        return lebar;
    }

    @Override
    public String toString() {
        return "tinggi: " + getTinggi() +
                "  - panjang: " + getPanjang() +
                " - lebar: " + getLebar();
    }

    public T getResultAsLong(){
        T = getPanjang() * getLebar();
    }
}

In getResultAslong method it should take Long data type, but I have a problem that the * operand cannot be used. What is the problem ?

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
Zein IF
  • 19
  • 4
  • 4
    How is T defined? – Ramesh Kotha Sep 23 '19 at 05:59
  • Possible duplicate of [Using a generic class to perform basic arithmetic operations](https://stackoverflow.com/questions/14046209/using-a-generic-class-to-perform-basic-arithmetic-operations) – Piro Sep 23 '19 at 06:03
  • 1
    quick answer: not possible to multiply instances of object; what should the result of multiplying, for example, `"a"` and `"bc"` be? – user85421 Sep 23 '19 at 06:22
  • the result of of the multiply should be the value of tinggi*panjang*lebar – Zein IF Sep 23 '19 at 06:32
  • @Piro oh i have to make abstract class. is there any otherway that i can use ? a method of Number class that can multiply ? – Zein IF Sep 23 '19 at 06:50
  • I believe your class is a generic class something like `public class Kubus {` ? it is better to give these kind of key code so people don't need to guess – Adrian Shum Sep 23 '19 at 07:19

3 Answers3

3

The first thing I would point out is that you can't directly define a class which only accepts a specific list of types. You can only bound T, say, to be a subclass of 1 type (or intersection of types).

For the types listed in the question, the only common supertypes of these is Serializable and Object. You could declare the type variable as:

class Kubus<T extends Serializable> { // or Kubus<T>, equivalent to T extends Object

but you can then instantiate with any T within this bound, e.g. Kubus<ArrayList<Frobnicator>>, since ArrayList implements Serializable. (Serializable is also a pretty useless type to bound on, unless you actually want to serialize Kubus using built-in serialization. To paraphrase Josh Bloch's advice in Effective Java: "don't").

If you want it only to accept those types, all you can do is to limit how the class can be constructed, by making the constructor private, and then providing static factory methods for the types you want:

private Kubus(T tinggi, T panjang,T lebar ) { ... }

public static Kubus<Integer> ofInteger(Integer tinggi, Integer panjang, Integer lebar ) {
  return new Kubus<>(tinggi, panjang, lebar);
}

public static Kubus<Long> ofLong(Long tinggi, Long panjang, Long lebar ) {
  return new Kubus<>(tinggi, panjang, lebar);
}

// etc for other specific types.

You can then create instances with

Kubus<Integer> first = Kubus.ofInteger(1, 2, 3);
Kubus<Long> second = Kubus.ofLong(1L, 2L, 3L);

It is still valid to write the type Kubus<ArrayList<Frobnicator>> (or whatever arbitrary type), but you just can't create an instance of it.

Kubus<ArrayList<Frobnicator>> whatever = null;

This actually gives you the opportunity to solve the specific problem that you asked about (*).

The issue is that you can't multiply arbitrary things together. Here, you need some means to convert a "thing" to a long in order to multiply it. Casting to Long is not a safe choice, because you can't cast, say, an Integer or a String to a Long.

Because you want to restrict T to only certain types, you can also provide a converter to do the conversion type-safely.

Such a converter can be modelled using a ToLongFuntion<T>: this takes a T, and returns a long. So, change your constructor to take another parameter:

private Kubus(T tinggi, T panjang,T lebar, ToLongFunction<T> converter ) { ... }

public static Kubus<Integer> ofInteger(Integer tinggi, Integer panjang, Integer lebar ) {
  return new Kubus<>(tinggi, panjang, lebar, i -> i.intValue());
}

public static Kubus<Long> ofLong(Long tinggi, Long panjang, Long lebar ) {
  return new Kubus<>(tinggi, panjang, lebar, lng -> lng.longValue());
}

// etc for other specific types.

Assign converter to a field in the constructor, and then apply in your multiplication method to get the value as a long, which can be multiplied:

return converter.applyAsLong(getTinggi())
    * converter.applyAsLong(getPanjang())
    * converter.applyAsLong(getLebar());

(*) I am not intending to imply that you can only use this converter approach with a private constructor: you can, of course, make the constructor non-private, and instantiate directly using the constructor. I just suspect that you'd very quickly get tired of the repetition in writing:

Kubus<Integer> first = new Kubus<>(1, 2, 3, Integer::longValue);
Kubus<Integer> second = new Kubus<>(1, 2, 3, Integer::longValue);
Kubus<Integer> third = new Kubus<>(1, 2, 3, Integer::longValue);
// ...
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Great answer. With help of overloading, it could be even more concise: just call the factory method `of` and then we can use the same method name for any supported types. It will be great to add the case for String, which looks like `public static Kubus of(String a, String b, String c) { return new Kubus<>(a, b, c, Long::parseLong); }` – Adrian Shum Sep 23 '19 at 07:26
  • thx for explaining this to me ... im hard to understand generic because it the first week in university and we have to implement generic . – Zein IF Sep 23 '19 at 07:30
  • @Andy Turner . how do i assignt converter to my constructor field ? – Zein IF Sep 23 '19 at 08:42
0

If you want to perform multiplication operation (*) then first you need to cast the values of getPanjang() and getLebar() to any number format then perform multiplication operation.

Abhishek Gharai
  • 227
  • 3
  • 15
0

If your T type needs to be a number then you could define it with <T extends Number> which would ensure you could call Number.longValue on any T. Unfortunately you specifically state that it might be a String (which presumably you want to convert to a long in getResultAsLong).

You have a few choices:

  1. don't use generics. Define an interface or an abstract class with a longValue method and then extend it for the various types you want to support.

  2. convert to long via String with Long.valueOf(getLabar().toString()). That essentially converts everything to a string before converting to a long. Not elegant.

  3. use instanceof Number and instanceof String to decide how to covert them to long.

sprinter
  • 27,148
  • 6
  • 47
  • 78
  • "Don't use generics" is the only appropriate choice here; but I would add that a new interface is unnecessary if you are modelling a cube, because a cube with non-numeric sides doesn't make sense. – Andy Turner Sep 23 '19 at 06:39
  • option 1 is not my choiche becuase we have to use generic im gonna try option 2 and 3 . thx for reply – Zein IF Sep 23 '19 at 06:44
  • @AndyTurner the question mentioned that the type could be a String. – sprinter Sep 23 '19 at 07:43