#Generics with Numbers

1 messages · Page 1 of 1 (latest)

gloomy sandal
#

I have two questions.

A) Need some code review on my thinking process (2 stages). What could I have done better at each stage, were there alternative things I could have done?

1. First attempt at Generics with numbers:

```
@FunctionalInterface
public interface NumberPredicate<T extends Number> {
    boolean test(T number);
}

public class NumberUtils {
    public static NumberPredicate isEven = x -> {
        if(x instanceof Double || x instanceof Float || x instanceof BigDecimal){
            BigDecimal num = BigDecimal.valueOf(0);
            if(x instanceof Double)
                num = BigDecimal.valueOf((Double)x);
            else if (x instanceof Float)
                num = BigDecimal.valueOf((Float)x);
            else if (x instanceof BigDecimal)
                num = (BigDecimal) x;
            return num.stripTrailingZeros().scale()<=0;
        }
        return (x.intValue() % 2 ==0);
    };
}
```

2. Second attempt at Generics with numbers:

```
@FunctionalInterface
public interface NumberPredicate<T extends Number> {
    boolean test(T number);
}

public class NumberUtils {
    public static NumberPredicate<Integer> isEvenInt = x -> x % 2 ==0;

    public static NumberPredicate<Double> isEvenDouble = x -> {
        while(x % 1 != 0)
            x*=10;
        return x % 2 == 0;
    };
    //assume i will implement a new method per number type
}
```

B) For my second question. When I call my method:

```
        List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        nums.stream().filter(NumberUtils.isEvenInt::test).collect(Collectors.toList()).forEach(System.out::println);
```

Why does it need to be called with `NumberUtils.isEvenInt::test` and not `NumberUtils.isEvenInt`?
harsh pelicanBOT
# gloomy sandal I have two questions. A) Need some code review on my thinking process (2 stages...

Detected code, here are some useful tools:

Formatted code
@FunctionalInterface
public interface NumberPredicate<T extends Number> {
  boolean test(T number);
}
public class NumberUtils {
  public static NumberPredicate isEven = x -> {
    if (x instanceof Double || x instanceof Float || x instanceof BigDecimal) {
      BigDecimal num = BigDecimal.valueOf(0);
      if (x instanceof Double) num = BigDecimal.valueOf((Double) x);
      else if (x instanceof Float) num = BigDecimal.valueOf((Float) x);
      else if (x instanceof BigDecimal) num = (BigDecimal) x;
      return num.stripTrailingZeros().scale() <= 0;
    }
    return (x.intValue() % 2 == 0);
  };
}
#

<@&987246883653156906> please have a look, thanks.

agile garnet
agile garnet
gloomy sandal
agile garnet
agile garnet
gloomy sandal
#

tf

agile garnet
agile garnet
#

it should be something like

#

public interface NumberPredicate extends Predicate<T extends Number>

#

or something like that

#

but anyway

#

this interface, NumberPredicate isn't really useful

gloomy sandal
#

I still don't really get why test removes the need to extend the Predicate

gloomy sandal
#

need to do public interface NumberPredicate<T extends Number> extends Predicate<T>

agile garnet
#

it should be

#
public interface NumberPredicate<T extends Number> extends Predicate<T> {}
#

there should be no method inside

#

@gloomy sandal

agile garnet
gloomy sandal
agile garnet
agile garnet
gloomy sandal
#

where should I put my implementation

agile garnet
#

no

gloomy sandal
#

I understand I'm wrong but I'm struggling to understand what is the correct one

agile garnet
#

remove it

#

it already exist

gloomy sandal
#

I should not have NumberUtils?

agile garnet
#

no

#

that's not what I said

#

I said isEven doesn't make sense

#

and also, not sure what the point of NumberPredicate is, this interface seems useless

gloomy sandal
#

ok, so what is the proper way from here

    @FunctionalInterface
    public interface NumberPredicate<T extends Number> extends Predicate<T> { }

    public class NumberUtils {
        public static NumberPredicate<Integer> isEvenInt = x -> x % 2 ==0;

        public static NumberPredicate<Double> isEvenDouble = x -> {
            while(x % 1 != 0)
                x*=10;
            return x % 2 == 0;
        };
        //assume i will implement a new method per number type
    }
    

agile garnet
#

but I don't see the point of NumberPredicate

gloomy sandal
#

I don't know what is wrong or missing, I followed a tutorial to get here

#

I don't follow

#

what is not seeing the point

#

where is there not a point

#

can you explain a bit more

agile garnet
#

what's the point of creating NumberPredicate when Predicate already exist ?

gloomy sandal
#

Like I said, it's what the tutorial told me to do
But I assume there must be some reason to create custom functional interfaces, otherwise Mosh wouldn't be teaching how to create them?

gloomy sandal
#

but I assume that the Mosh tutorial is teaching custom functional interface for a reason?

agile garnet
#

tho the isEven one is very very bad

#

also

gloomy sandal
agile garnet
#

your isEvenDouble is broken

agile garnet
#

usually, when you have instanceof, you are doing something wrong

#

especially if you are using generics

#

because the point of working with generics is to not use instanceof

agile garnet
gloomy sandal
agile garnet
#

so yea

#

you can't do that with double

#

the best you could do is to truncate whatever is behind the . and then test if it is even

#

basically (int)x % 2 == 0

gloomy sandal
#

yea, the chatgpt solution

#

but gpt gave me some long and incorrect explanation

#

thank you

#

is there a reason to implement a custom functional interface ever

#

I accepted that the NumberPredicate is bad

#

but surely there could be some other reason to create a functional interface?

agile garnet
#

implement or write ?

#

ok so write

#

well

#

I rarelly ever wrote one

#

maybe you are in a situation where you specifically want that

#

but it's quite rare

#

there are like 3 cases

#
  • handling primitives
  • simplifying code and giving more obvious thing
  • give additional behavior
#

usually 2 and 3 come together

#

handling primitives is a special one

#

because generics don't work on primitives, so sometime you want something special for them

#

for the second point, creating a functional interface for a specific reason (basically here NumberPredicate) may make it more obvious and so easier to use, especially with complex code in a library

#

and for the third one

#

it's probably the most common case, and also the one where you would have the most reasons, examples of such cases exist, like Iterable

#

where you want not just a function with certain matching parameters and return type

#

but also specific methods

#

look at Iterable, it could have been a Supplier, yet it isn't

#

because Iterable is way more than that

#

it has forEach method for example

#

@gloomy sandal

gloomy sandal
#

but Collections is not really a functional interface is it?

#

wait nvm

#

its Iterable interface

agile garnet
#

yes, Iterable is an interface

gloomy sandal
#

However, Iterable contains more than one abstract method (iterator() and spliterator()), so it does not qualify as a functional interface.

agile garnet
#

it may have been needed, it could have simply been a Supplier

agile garnet
#

it only has one

#

check the doc

echo boughBOT
#
public interface Iterable<T>

Implementing this interface allows an object to be the target of the enhanced for statement (sometimes called the "for-each loop" statement).

jls

14.14.2 The enhanced for statement

param

the type of elements returned by the iterator

since

1.5

gloomy sandal
#

oh I see, the rest are instance/default methods

#

there is only one abstract

#

even google top result is wrong information, it says that Iterable is not a functional interface

agile garnet
#

because Iterable doesn't have the @FunctionalInterface annotation

#

but that doesn't mean that it can't be used as a functional interface

#
Iterable<Integer> range(int min, int max) {
  return IntStream.range(min, max)::iterator;
}
// Then
for(int i : range(0, 10)) {
  println(i);
}

You can for example do this

gloomy sandal
#

I thought that annotations don't really mean anything they are just there to give warnings if the code author does something they don't like/transgresses the rules

agile garnet
#

anyway, i have to go to sleep

gloomy sandal
gloomy sandal
#

ok nvm i got it to work

#


        for(int i : range(0, 10)) {
            System.out.println(i);
        }

    }
    public static Iterable<Integer> range(int min, int max) {
        return () -> IntStream.range(min, max).iterator();
    }```
harsh pelicanBOT
harsh pelicanBOT
#

@gloomy sandal

Your question has been closed due to inactivity.

If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.

Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.

When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.

Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.

With enough info, someone knows the answer for sure 👍

gloomy sandal
# agile garnet usually, when you have instanceof, you are doing something wrong

Sorry to necro this, but can I clarify why using Generics with instanceOf is bad?

I just saw my friend (experienced programmer) do this

    public <T> OutPacket encode(T in){
        switch(in){
            case Boolean value -> buffer.writeBoolean(value);
            case Byte value -> buffer.writeByte(value);
            case Short value -> buffer.writeShortLE(value);
            case Integer value -> buffer.writeIntLE(value);
            case Long value -> buffer.writeLongLE(value);
            case Point value -> {
                buffer.writeShortLE(value.getY());
                buffer.writeShortLE(value.getY());
            }
            case String value -> {
                buffer.writeShortLE(value.length());
                buffer.writeBytes(value.getBytes(StandardCharsets.US_ASCII));
            }
            case byte[] value -> buffer.writeBytes(value);
            default -> throw new IllegalStateException(STR."Unexpected value: \{in}");
        }
        
        return this;
    }

Isn't using a switch case with wrappers basically doing a conditional with instanceOf? Is this code wrong in a similar sense of why my original design was wrong to check for specific types in generics?

harsh pelicanBOT
# gloomy sandal Sorry to necro this, but can I clarify why using Generics with `instanceOf` is b...

Detected code, here are some useful tools:

Formatted code
public <T> OutPacket encode(T in) {
  switch (in) {
    case Boolean value -> buffer.writeBoolean(value);
    case Byte value -> buffer.writeByte(value);
    case Short value -> buffer.writeShortLE(value);
    case Integer value -> buffer.writeIntLE(value);
    case Long value -> buffer.writeLongLE(value);
    case Point value -> {
      buffer.writeShortLE(value.getY());
      buffer.writeShortLE(value.getY());
    }
    case String value -> {
      buffer.writeShortLE(value.length());
      buffer.writeBytes(value.getBytes(StandardCharsets.US_ASCII));
    }
    case byte [] value -> buffer.writeBytes(value);
    default  -> throw new IllegalStateException(STR."Unexpected value: \{in}");
  }
  return this ;
}
agile garnet
# gloomy sandal Sorry to necro this, but can I clarify why using Generics with `instanceOf` is b...

The point of a generic is to say, "it doesn't matter what the type of t is, I'll threat it the same no matter what it is", you want to make it generic, quite literally... which isn't what you do here, you are assuming the type.
Ah and also note that here, you have 0 compile time check which ensure that T is a correct type. There is no difference between using T here or Object.
And if you know Object, you should know that using it as a type is usually bad... you are quite literaly saying "you can give anything to this method, but if it is a wrong type, it will crash, and even if it is a good type, it will do different things"
The correct approach to this problem is to declare an interface that is implemented in several ways

#

also, don't use STR, it will be removed in java 23

gloomy sandal
agile garnet
#

there are two approaches

  • Object Oriented
  • Data Oriented
  • Alternatively, Object Oriented with Visitor pattern
#

Object Oriented approach:

interface EncodeValue {
  void encode(MyBuffer buffer);
}
record BoolValue(boolean val) implements EncodeValue {
  @Override
  void encode(MyBuffer buffer) {
    buffer.writeBoolean(val);
  }
}
record ByteValue(byte val) implements EncodeValue {
  @Override
  void encode(MyBuffer buffer) {
    buffer.writeByte(val);
  }
}
etc

Then

public OutPacket encode(EncodeValue val) {
  val.encode(buffer);
  return this;
}
#

Data Oriented approach:

sealed interface EncodeValue permits BoolValue, ByteValue /*(etc, not needed if same package)*/ {
}
record BoolValue(boolean val) implements EncodeValue {}
record ByteValue(byte val) implements EncodeValue {}
etc

Then

public OutPacket encode(EncodeValue val) {
  switch(val) {
    case BoolValue(boolean v) -> buffer.writeBoolean(v);
    case ByteValue(byte v) -> buffer.writeByte(v);
    etc
    // No default here, cause you have compile time guarranty that it can't be anything else
  }
  return this;
}
#

And like I said, alternatively, visitor pattern could also work

#

@gloomy sandal

gloomy sandal
# agile garnet And like I said, alternatively, visitor pattern could also work

When you say Visitor Pattern, you mean to have some interface EncodedItem and then have concrete implementations with accept for each cases like class BooleanItem implements EncodedItem { public void accept(EncoderVisitor visitor){...} } and so forth

Then an EncoderVisitor interface and concrete implementations for each one?

But then that is like writing 8 * 2 concrete implementations for the EncodedValue types being handled not including the interfaces, and each implementation will only have one method

#

Also, if branching from a "good type" whether bounded or not is a wrong application of generics, then when is a right application of generics?

agile garnet
#

when you threat all the types the same way

#

when you threat it in a generic way

agile garnet
agile garnet
gloomy sandal
agile garnet
gloomy sandal
agile garnet
agile garnet
gloomy sandal
agile garnet
#

20 isn't

gloomy sandal
# agile garnet Data Oriented approach: ```java sealed interface EncodeValue permits BoolValue, ...

So if I am understanding correctly (after rereading a couple of times) the goal is to catch errors at compile rather than runtime?

If so, why is that better design as compared to throwing an exception in the default case in the original code?

We could still handle the exception, and furthermore the exception would indicate that someone somewhere had used this method wrong/unintentionally (vs them not being able to use it wrongly in the first place?)

agile garnet
#

or if you prefer a more important question which has the same answer : why does java have static types ?