0

I am trying to have a Flux generic converter using generic types in Java 8. I am basing my code on this answer. Ths idea is basically to implement this specific converter String -> Integer:

    public Flux<Integer> createFluxConverterStringToInt(String[] data) {
        Flux<Integer> stringFlux = Flux
                .just(data)
                .map(value -> Integer.parseInt(value))
                .log();
        return stringFlux;
    }

to a typed converter String -> to whatever type I want. So I am creating a class FluxAndMonoGeneric of type <OUT> with a constructor FluxAndMonoGeneric(Class<OUT> typeArgumentClass), and returning a method public Flux<OUT> createFluxConverter(String[] data). I was thinking to create multiples entries like this : this.typeArgumentClass.isInstance(Integer.class) on the map call but with different class types. But it is not working even for an Integer.

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Slf4j
public class FluxAndMonoGeneric<OUT> {

    private final Class<OUT> typeArgumentClass;

    public FluxAndMonoGeneric(Class<OUT> typeArgumentClass) {
        this.typeArgumentClass = typeArgumentClass;
    }

    public Flux<OUT> createFluxConverter(String[] data) {
        Flux<OUT> stringFlux = Flux
                .just(data)
                .map(value -> {
                    // NEVER ENTERS ON THIS IF
                    if (this.typeArgumentClass.isInstance(Integer.class))
                        return this.typeArgumentClass.cast(Integer.parseInt(value));
                    // ALWAYS ENTERS ON THIS ELSE AND STRING CANNOT BE CASTED TO INTEGER
                    else return this.typeArgumentClass.cast(value);
                })
                .log();
        return stringFlux;
    }

    public Mono<OUT> createMonoConverter(String data) {
        Mono<OUT> integerMono = Mono.just(data)
                .map(value -> this.typeArgumentClass.cast(value))
                .log();
        return integerMono;
    }
}

The I am getting the error: java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Integer when I test using this unit test.

import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
public class FluxAndMonoGenericTest {
    // this works.
    @Test
    void testCreateGenericFluxWithString() {

        String expect = "Spring,Spring Boot,Reactive Spring";
        String delimiter = ",";
        String[] messages = expect.split(delimiter);

        FluxAndMonoGeneric<String> myFluxGenericTest = new FluxAndMonoGeneric<String>(String.class);

        Flux<String> stringFlux = myFluxGenericTest.createFluxConverter(messages);

        StepVerifier.create(stringFlux)
                .expectNext(messages[0])
                .expectNext(messages[1])
                .expectNext(messages[2])
                .verifyComplete();
    }
    // THIS DOES NOT WORK
    @Test
    void testCreateGenericFluxWithInteger() {

        String expect = "1,1234,7654,34";
        String delimiter = ",";
        String[] messages = expect.split(delimiter);
        Integer[] expectedMessages = new Integer[]{1, 1234, 7654, 34};

        FluxAndMonoGeneric<Integer> myFluxGenericTest = new FluxAndMonoGeneric<Integer>(Integer.class);

        Flux<Integer> stringFlux = myFluxGenericTest.createFluxConverter(messages);

        StepVerifier.create(stringFlux)
                .expectNext(expectedMessages[0])
                .expectNext(expectedMessages[1])
                .expectNext(expectedMessages[2])
                .expectNext(expectedMessages[3])
                .verifyComplete();
    }
}
Felipe
  • 7,013
  • 8
  • 44
  • 102
  • `this.typeArgumentClass.isInstance(Integer.class)` should be `this.typeArgumentClass.equals(Integer.class)` – samabcde Mar 11 '21 at 14:24

1 Answers1

1
    public <T> Flux<T> createFluxConverterStringToInt(String[] data, Function<String, T> mapper) {
        return Flux
                .just(data)
                .map(mapper)
                .log();
    }

Then, rather than passing in the type of output you want, you just pass in whichever mapper you want to convert the data into the right output type.

E.g. Integer::parseInt or String::trim or whatever.

Ashley Frieze
  • 4,993
  • 2
  • 29
  • 23