I got sick and tired of type-related errors and decided to write a type conversion service

It's not particularly pretty what with those casts and suppressions, but it seems to do what it was designed for

A few things to note:

1. I didn't write javadocs: those are to be written in a non-English language. The most important thing to know is `S` means "source type", `T` means "target type"
2. In case you're wondering, I had to introduce the `S` generic type parameter in `Converter` solely for this to work:
```java
    @Override
    @SuppressWarnings("unchecked")
    public <S> Optional<T> convert(S value) {
        Class<?> valueType = value.getClass();
        // I can't write <? super valueType>
        Function<? super S, Optional<T>> mapping = (Function<? super S, Optional<T>>) getMappingForType(valueType);
        // if it was an unbounded wildcard, this line wouldn't compile
        Optional<T> optionalValue = mapping.apply(value);
        return optionalValue;
    }
```
3. I realize I don't account for overflows and `NumberFormatExeption`s. If truth be told, I couldn't figure out a good way to handle that

*(should `ConverterService` catch? should it be the responsibility of individual `Converter`s? how do I make it not ugly? try-catches are ugly as is, let alone in lambdas)*

Let it throw, for the time being (or forever)

How can I improve it?

I was hoping to make it cleaner and more robust.

**`Converter`:**

```java
package solution.common.converter;

import java.util.Optional;

public interface Converter<T> {
    <S> Optional<T> convert(S value);
}
```

**`Converter` implementation:**

```java
package solution.common.converter;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

public class BasicConverter<T> implements Converter<T> {
    private final Map<Class<?>, Function<?, Optional<T>>> mappings = new HashMap<>();

    public <S> void addMapping(Class<S> sourceType, Function<S, Optional<T>> mappingFunction) {
        mappings.put(sourceType, mappingFunction);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <S> Optional<T> convert(S value) {
        Class<?> valueType = value.getClass();
        Function<? super S, Optional<T>> mapping = (Function<? super S, Optional<T>>) getMappingForType(valueType);
        Optional<T> optionalValue = mapping.apply(value);
        return optionalValue;
    }

    private Function<?, Optional<T>> getMappingForType(Class<?> type) {
        Function<?, Optional<T>> mapping = mappings.containsKey(type) ?
                mappings.get(type) :
                findSuperTypeMapping(type);
        return mapping;
    }

    @SuppressWarnings("unchecked")
    private <S> Function<? super S, Optional<T>> findSuperTypeMapping(Class<S> valueType) {
        for (Class<?> aClass : mappings.keySet()) {
            if (aClass.isAssignableFrom(valueType))
                return (Function<? super S, Optional<T>>) mappings.get(aClass);
        }
        return noopMapping();
    }

    private <S> Function<S, Optional<T>> noopMapping() {
        return input -> Optional.empty();
    }
}
```

**`ConverterService`:**

```java
package solution.common.converter;

import java.util.Optional;

public interface ConverterService {
    <T> Optional<T> convert(Object value, Class<T> targetType);
}
```

**`ConverterService` implementation:**

```java
package solution.common.converter;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class BasicConverterService implements ConverterService {
    private final Map<Class<?>, Converter<?>> converterMap = new HashMap<>();

    public <T> void addConverter(Class<T> targetType, Converter<T> converter) {
        converterMap.put(targetType, converter);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Optional<T> convert(Object value, Class<T> targetType) {
        if (targetType.isAssignableFrom(value.getClass()))
            return (Optional<T>) Optional.of(value);
        Converter<?> converter = (converterMap.containsKey(targetType)) ?
                converterMap.get(targetType) :
                findSubtypeConverter(targetType);
        return (Optional<T>) converter.convert(value);
    }

    private Converter<?> findSubtypeConverter(Class<?> targetType) {
        for (Class<?> aClass : converterMap.keySet()) {
            if (targetType.isAssignableFrom(aClass))
                return converterMap.get(aClass);
        }
        return noopConverter();
    }

    private <T> Converter<T> noopConverter() {
        return new Converter<T>() {
            @Override
            public <S> Optional<T> convert(S value) {
                return Optional.empty();
            }
        };
    }
}
```

**Tests:**

```java
package solution.common.converter;

import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

class BasicConverterServiceTest {
    @Test
    void convert_ifNoConvertersAdded_andTargetTypeDoesNotMatchSourceType_returnsEmptyOptional() {
        BasicConverterService converterService = new BasicConverterService();
        Optional<Integer> valueOptional = converterService.convert("42", Integer.class);
        assertFalse(valueOptional.isPresent());
    }

    @Test
    void convert_ifNoConverterAdded_butTargetTypeMatchesSourceType_returnsOptionalOfInputValue() {
        BasicConverterService converterService = new BasicConverterService();
        Optional<String> valueOptional = converterService.convert("42", String.class);
        assertTrue(valueOptional.isPresent());
        assertEquals("42", valueOptional.get());
    }

    @Test
    void convert_ifConverterMatchingTypeAdded_returnsOptionalOfThatConvertersResult() {
        BasicConverterService converterService = new BasicConverterService();
        converterService.addConverter(Integer.class, new Converter<Integer>() {
            @Override
            public <S> Optional<Integer> convert(S value) {
                return Optional.of(Integer.parseInt((String) value));
            }
        });
        Optional<Integer> valueOptional = converterService.convert("42", Integer.class);
        assertTrue(valueOptional.isPresent());
        assertEquals(42, valueOptional.get());
    }

    @Test
    void convert_ifConverterMatchingSubtypeAdded_returnsOptionalOfThatConvertersResult() {
        BasicConverterService converterService = new BasicConverterService();
        converterService.addConverter(Integer.class, new Converter<Integer>() {
            @Override
            public <S> Optional<Integer> convert(S value) {
                return Optional.of(Integer.parseInt((String) value));
            }
        });
        Optional<Number> valueOptional = converterService.convert("42", Number.class);
        assertTrue(valueOptional.isPresent());
        assertEquals(42, valueOptional.get());
    }
}
```

Here's a factory that returns a more fully-featured `ConverterService`. It seems to cover most, if not all, of our project's needs. It contains one line that doesn't compile, the one that involves `DateUtils`, but I decided to omit that utility for the purpose of this code review. Basically, it checks if the input object is `Date` and casts if so; if it's a string, it tries a number of date formats to parse it, one by one

```java
package solution.common.di;

import solution.common.converter.BasicConverter;
import solution.common.converter.BasicConverterService;
import solution.common.converter.Converter;
import solution.common.converter.ConverterService;
import solution.common.date.DateUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Optional;

public class Converters {
    public static ConverterService converterService() {
        BasicConverterService converterService = new BasicConverterService();
        converterService.addConverter(Date.class, toDateConverter());
        converterService.addConverter(Byte.class, toByteConverter());
        converterService.addConverter(Short.class, toShortConverter());
        converterService.addConverter(Integer.class, toIntegerConverter());
        converterService.addConverter(BigInteger.class, toBigIntegerConverter());
        converterService.addConverter(Long.class, toLongConverter());
        converterService.addConverter(Float.class, toFloatConverter());
        converterService.addConverter(Double.class, toDoubleConverter());
        converterService.addConverter(BigDecimal.class, toBigDecimalConverter());
        converterService.addConverter(String.class, toStringConverter());
        return converterService;
    }

    private static Converter<Date> toDateConverter() {
        BasicConverter<Date> converter = new BasicConverter<>();
        converter.addMapping(Object.class, DateUtils::asDate);
        return converter;
    }

    private static Converter<Byte> toByteConverter() {
        BasicConverter<Byte> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Byte::parseByte));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::byteValue));
        return converter;
    }

    private static Converter<Short> toShortConverter() {
        BasicConverter<Short> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Short::parseShort));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::shortValue));
        return converter;
    }

    private static Converter<Integer> toIntegerConverter() {
        BasicConverter<Integer> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Integer::parseInt));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::intValue));
        return converter;
    }

    private static Converter<Long> toLongConverter() {
        BasicConverter<Long> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Long::parseLong));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::longValue));
        return converter;
    }

    private static Converter<BigInteger> toBigIntegerConverter() {
        BasicConverter<BigInteger> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(BigInteger::new));
        converter.addMapping(Number.class, input -> Optional.of(input)
                .map(Number::longValue)
                .map(BigInteger::valueOf));
        return converter;
    }

    private static Converter<Float> toFloatConverter() {
        BasicConverter<Float> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Float::parseFloat));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::floatValue));
        return converter;
    }

    private static Converter<Double> toDoubleConverter() {
        BasicConverter<Double> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(Double::parseDouble));
        converter.addMapping(Number.class, input -> Optional.of(input).map(Number::doubleValue));
        return converter;
    }

    private static Converter<BigDecimal> toBigDecimalConverter() {
        BasicConverter<BigDecimal> converter = new BasicConverter<>();
        converter.addMapping(String.class, input -> Optional.of(input)
                .map(BigDecimal::new));
        converter.addMapping(Number.class, input -> Optional.of(input)
                .map(Number::doubleValue)
                .map(BigDecimal::new));
        return converter;
    }

    private static Converter<String> toStringConverter() {
        BasicConverter<String> converter = new BasicConverter<>();
        converter.addMapping(Object.class, input -> Optional.of(input).map(Object::toString));
        return converter;
    }
}
```