删除代码重复

one*_*one 9 java generics programming-languages functional-programming

我正在尝试为Java创建一个小函数式编程库(只是为了抓住我自己的痒).在为s,s和s 定义高阶函数时List,我遇到了这个问题:采用集合并返回相同类型集合的函数具有几乎相同的实现,但必须为每个函数重新定义数据结构 - s,s和s.SetMapListSetMap

例如,这里是s和s 的map函数的实现:ListSet

public static <A, B> List<B> map(
  List<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  List<B> ys = new ArrayList<B>();
  for(A a : xs) {
    ys.add(transformer.apply(a));
  }
  return ys;
}

public static <A, B> Set<B> map(
  Set<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  Set<B> ys = new HashSet<B>();
  for(A a : xs) {
    ys.add(transformer.apply(a));
  }
  return ys;
}
Run Code Online (Sandbox Code Playgroud)

一个filter函数:

public static <A> List<A> filter(
  List<? extends A> xs, 
  Func1<? super A, Boolean> predicate
) {
  List<A> ys = new ArrayList<A>();
  for(A a : xs) {
    if(predicate.apply(a)) {
      ys.add(a);
    }
  }
  return ys;
}

public static <A> Set<A> filter(
  Set<? extends A> xs, 
  Func1<? super A, Boolean> predicate
) {
  Set<A> ys = new HashSet<A>();
  for(A a : xs) {
    if(predicate.apply(a)) {
      ys.add(a);
    }
  }
  return ys;
}
Run Code Online (Sandbox Code Playgroud)

从该示例可以看出,实现的主体SetList几乎相同.

有喜欢很多很多的功能map,并filter在我的图书馆,而且每个是针对每一类藏品我感兴趣的三次定义(即List,SetMap).这会导致很多代码重复和代码异味.我想知道Java中是否有某种方法可以帮助我避免所有的代码重复.

任何帮助将不胜感激.谢谢.

编辑:

Func1 是一个定义为的接口:

interface Func1<A, B> {
  public B apply(A a);
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*ine 6

public static <A, B> List<B> map(
  List<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  List<B> ys = new ArrayList<B>();
  map(xy, transformer, ys);
  return ys;
}

public static <A, B> Set<B> map(
  Set<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  Set<B> ys = new HashSet<B>();
  map(xy, transformer, ys);
  return ys;
}
private static <A, B> map(
  Collection<? extends A> xs, 
  Func1<? super A, ? extends B> transformer,
  Iterable<B> ys
) {
  for(A a : xs) {
    ys.add(transformer.apply(a));
  }
}
Run Code Online (Sandbox Code Playgroud)

任务完成.

注意,这是典型的Java API,要将集合中的mutable传递给它,而不是在方法中创建一个新的.就个人而言,我不是集合级别的可变性的粉丝,但它是我们必须使用的(在Java中).

(我不喜欢AB作为这类东西的通用参数.)

或者你可以使用工厂:

public static <A, B> List<B> map(
  List<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  return map(xs, transformer, new CollectionFactory<B, List<B>>() {
      public List<B> create() { return new ArrayList<B>(); }
  });
}

public static <A, B> Set<B> map(
  Set<? extends A> xs, 
  Func1<? super A, ? extends B> transformer
) {
  return map(xs, transformer, new CollectionFactory<B, Set<B>>() {
      public Set<B> create() { return new HashSet<B>(); }
  });
}

private interface CollectionFactory<E, C extends Collection<E>> {
    C create();
}

private static <A, B, C extends Collection<B>> C map(
  Iterable<? extends A> xs, 
  Func1<? super A, ? extends B> transformer,
  CollectionFactory<B, C> factory
) {
  C ys = factory.create();
  for(A a : xs) {
    ys.add(transformer.apply(a));
  }
  return ys;
}
Run Code Online (Sandbox Code Playgroud)

(如果你能忍受匿名内部类的毫无意义的冗长.)

如果它不适合Collection那么你需要放一些(丑陋的)适配器.

为了完整性(虽然未经过测试,可以进行一些调整),使用继承的令人不快的解决方案:

Set<String> strs = hashSets().map(things, formatter);

...

public static <E> Functions<E, Set<E>> hashSets() {
    return new Functions<E, Set<E>>() {
        protected Set<E> createCollections() {
            return new HashSet<E>();
        }
    };
}

public abstract class Functions<E, C extends Collection<E>> {
    protected abstract C createCollection();

    public <S> C map(
      Set<? extends S> xs, 
      Func1<? super S, ? extends E> transformer
    ) {
      C ys = createCollection();
      for(S a : xs) {
        ys.add(transformer.apply(a));
      }
      return ys;
    }

    public <S> C filter(
      List<? extends S> xs, 
      Func1<? super S, Boolean> predicate // Predicate<? super S> might be nicer!!
    ) {
      C ys = createCollection();
      for(A a : xs) {
        if(predicate.apply(a)) {
          ys.add(a);
        }
      }
      return ys;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ton*_*ris 4

Java 没有高阶多态性(又名高阶多态性),因此这在类型系统中是不可能的。许多Java 程序员求助于XML 和/或反射(即转义类型系统)来解决这一缺陷。

Scala 可以处理这个问题,您所描述的称为协变函子。这种相当基本的数据类型(以及更多)已在 Scalaz 库中实现,并包括 java.util.* 的实现。

此外,还有更多的协变函子不是集合,还有更多的函子不是协变的。

如果您想进一步探索这个特定的概念,您可能希望在 google 上搜索“20 个中级 Scala 练习”。