转换列表列表(或任何扩展 Number 的类)

发布于 2024-10-01 00:53:29 字数 1987 浏览 4 评论 0原文

我想创建一个非常通用的实用方法来获取任何 Collection 并将其转换为从 Number(长整型、双精度、浮点型、整数等)扩展的用户可选类的集合。

我想出了这段使用 Google Collections 的代码转换集合并返回不可变列表。

import java.util.List;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
     * Takes a {@code List<String>} and transforms it into a list of the
     * specified {@code clazz}.
     * 
     * @param <T>
     * @param stringValues
     *            the list of Strings to be used to create the list of the
     *            specified type
     * @param clazz
     *            must be a subclass of Number. Defines the type of the new List
     * @return
     */
    public static <T extends Number> List<T> toNumberList(List<String> stringValues, final Class<T> clazz) {
        List<T> ids = Lists.transform(stringValues, new Function<String, T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T apply(String from) {
                T retVal = null;
                if (clazz.equals(Integer.class)) {
                    retVal = (T) Integer.valueOf(from);
                } else if (clazz.equals(Long.class)) {
                    retVal = (T) Long.valueOf(from);
                } else if (clazz.equals(Float.class)) {
                    retVal = (T) Float.valueOf(from);
                } else if (clazz.equals(Double.class)) {
                    retVal = (T) Double.valueOf(from);
                } else {
                    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
                }
                return retVal;
            }
        });
        return ImmutableList.copyOf(ids);
    }

它可以这样使用:

// Convert List<String> to List<Long>
List<Long> ids = MiscUtils.toNumberList(productIds, Long.class);

我的代码是否太过分了,或者您将如何简化它并同时保持它足够通用?

I want to create a very generic utility method to take any Collection and convert it into a Collection of a user selectable class that extends from Number (Long, Double, Float, Integer, etc.)

I came up with this code that uses Google Collections to transform the Collection and to return an Immutable List.

import java.util.List;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
     * Takes a {@code List<String>} and transforms it into a list of the
     * specified {@code clazz}.
     * 
     * @param <T>
     * @param stringValues
     *            the list of Strings to be used to create the list of the
     *            specified type
     * @param clazz
     *            must be a subclass of Number. Defines the type of the new List
     * @return
     */
    public static <T extends Number> List<T> toNumberList(List<String> stringValues, final Class<T> clazz) {
        List<T> ids = Lists.transform(stringValues, new Function<String, T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T apply(String from) {
                T retVal = null;
                if (clazz.equals(Integer.class)) {
                    retVal = (T) Integer.valueOf(from);
                } else if (clazz.equals(Long.class)) {
                    retVal = (T) Long.valueOf(from);
                } else if (clazz.equals(Float.class)) {
                    retVal = (T) Float.valueOf(from);
                } else if (clazz.equals(Double.class)) {
                    retVal = (T) Double.valueOf(from);
                } else {
                    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
                }
                return retVal;
            }
        });
        return ImmutableList.copyOf(ids);
    }

It can be used like this:

// Convert List<String> to List<Long>
List<Long> ids = MiscUtils.toNumberList(productIds, Long.class);

Is my code overkill or how would you simplify it and at the same time keep it generic enough?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

戒ㄋ 2024-10-08 00:53:29

我认为这段代码最重要的方面是函数,而不是方法本身。我也不认为切换 Function 主体中允许的子类是没有意义的,因为您已经知道当时想要返回什么类型的 Number 函数已创建。如果给定 BigInteger.class,您的方法会失败,这也有一点问题。

鉴于此,我要做的是创建一个实用程序类(我们称之为 Numbers)并提供其上的方法,每个方法返回一个 Function (可以是一个 enum 单例)用于将 String 解析为特定类型的 Number。也就是说:

public class Numbers {
  public static Function<String, Integer> parseIntegerFunction() { ... }
  public static Function<String, Long> parseLongFunction() { ... }
  ...
}

它们都可以像这样实现:

public static Function<String, Integer> parseIntegerFunction() {
  return ParseIntegerFunction.INSTANCE;
}

private enum ParseIntegerFunction implements Function<String, Integer> {
  INSTANCE;

  public Integer apply(String input) {
    return Integer.valueOf(input);
  }

  @Override public String toString() {
    return "ParseIntegerFunction";
  }
}

然后可以按照用户想要的方式使用:

List<String> strings = ...
List<Integer> integers = Lists.transform(strings, Numbers.parseIntegerFunction());

这种方法比您的方法有很多优点:

  • 不需要在 Function 中进行任何切换...我们知道我们正在创建什么类型的数字,然后就这样做。快点。
  • 更灵活,因为每个 Function 可以在任何地方使用...用户不必像您的方法那样使用它(将转换后的值复制到 ImmutableList 您只创建您真正想要允许的Function
  • 如果没有BigInteger 解析函数,用户就无法调用它,而不是让它完全合法。在编译时执行此操作,然后在运行时失败,就像您的示例一样

,我建议将返回 ImmutableList 的任何方法的返回类型设置为 ImmutableList 而不是 List...它提供对方法的客户端有用的信息

编辑:

如果您确实需要更动态的东西。 (即您希望具有某些 Class 实例的类能够将 String 转换为该 Number 类型)您还可以添加一个查找方法,例如:

public static <T extends Number> Function<String, T> parseFunctionFor(Class<T> type) {
  // lookup the function for the type in an ImmutableMap and return it
}

但是,如果您没有为其提供 FunctionNumber 子类,则这与原始方法具有相同的问题。似乎在很多情况下这都不会有用。

I think the most important aspect of this code is the Function as opposed to the method itself. I also don't think it makes sense to switch over the subclasses you allow in the Function body, as you already know what type of Number you want to return at the time the Function is created. It's also slightly problematic that your method fails if given, say, BigInteger.class.

Given this, what I would do is create a utility class (let's call it Numbers) and provide methods on it that each return a Function (which can be an enum singleton) for parsing a String as a specific type of Number. That is:

public class Numbers {
  public static Function<String, Integer> parseIntegerFunction() { ... }
  public static Function<String, Long> parseLongFunction() { ... }
  ...
}

They could each be implemented something like this:

public static Function<String, Integer> parseIntegerFunction() {
  return ParseIntegerFunction.INSTANCE;
}

private enum ParseIntegerFunction implements Function<String, Integer> {
  INSTANCE;

  public Integer apply(String input) {
    return Integer.valueOf(input);
  }

  @Override public String toString() {
    return "ParseIntegerFunction";
  }
}

This can then be used however users want:

List<String> strings = ...
List<Integer> integers = Lists.transform(strings, Numbers.parseIntegerFunction());

This approach has quite a few advantages over yours:

  • Doesn't require any switching in the Function... we know what type of number we're creating and just do that. Faster.
  • Is more flexible, in that each Function can be used wherever... users aren't forced to use it the way your method does (copying the transformed values into an ImmutableList.
  • You only create the Functions you actually want to allow. If there's no BigInteger parsing function, users just can't call that, as opposed to having it be completely legal to do that at compile time and then fail at runtime like in your example.

As a side note, I'd recommend making the return type of any method that returns an ImmutableList be ImmutableList rather than List... it provides information that is useful to clients of the method.

Edit:

If you really need something more dynamic (i.e. you want classes that have an instance of some Class<T extends Number> to be able to transform Strings to that Number type) you could also add a lookup method like:

public static <T extends Number> Function<String, T> parseFunctionFor(Class<T> type) {
  // lookup the function for the type in an ImmutableMap and return it
}

This has the same problems as your original method, though, if there's a Number subclass that you don't provide a Function for. It also doesn't seem like there would be many situations where this would be useful.

塔塔猫 2024-10-08 00:53:29

为什么不实现几个转换器函数并将它们传递给 Lists.transform() 调用?

    public class IntegerTransformer extends Function<String, Integer>() {
        public Integer apply(String from) {
            return Integer.valueOf(from);
        }
    }

所以,你可以这样写:

Lists.transform(stringValues, new IntegerTransformer());

如果你想自动处理类型,你可以添加一个变压器工厂或一个映射:

static Map<Class,Function<String,?>> transformers = new HashMap<String,?>();
static {
  transformers.put(Integer.class, new IntegerTransformer());
  transformers.put(Integer.class, new LongTransformer());
  ...
}

public static Function<String,?> get(Class c) {
  Function<String,?> transformer = transformers.get(c);
  if(transformer==null) {
    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
  }
  return transformer;         
}

Why don't you implement several transformer functions and pass them to Lists.transform() call?

    public class IntegerTransformer extends Function<String, Integer>() {
        public Integer apply(String from) {
            return Integer.valueOf(from);
        }
    }

So, you could write:

Lists.transform(stringValues, new IntegerTransformer());

If you want to handle types automatically, you can add a transformer factory or a map:

static Map<Class,Function<String,?>> transformers = new HashMap<String,?>();
static {
  transformers.put(Integer.class, new IntegerTransformer());
  transformers.put(Integer.class, new LongTransformer());
  ...
}

public static Function<String,?> get(Class c) {
  Function<String,?> transformer = transformers.get(c);
  if(transformer==null) {
    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
  }
  return transformer;         
}
错々过的事 2024-10-08 00:53:29

我觉得不错。

既然您拥有 Class 令牌,为什么不避免未经检查的强制转换从而抑制警告呢?

retVal = clazz.cast(Double.valueOf(from)); 

Looks good to me.

Since you have the Class token, why not avoid the unchecked cast and thus suppress warnings?

retVal = clazz.cast(Double.valueOf(from)); 
叫思念不要吵 2024-10-08 00:53:29

您可以使用反射,并执行以下操作:

      Method m = clazz.getDeclaredMethod("valueOf", String.class);
      T str = (T) m.invoke(null, from);
      return str;

未经测试且可能很慢。

You could use reflection, and do something like this:

      Method m = clazz.getDeclaredMethod("valueOf", String.class);
      T str = (T) m.invoke(null, from);
      return str;

Untested and possible slow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文