作報表時,有時須要根據不一樣的業務生成不一樣的報表。這樣,須要可以動態地配置列字段,並根據列字段來輸出對應的報表。使用函數接口結合枚舉能夠比較優雅地實現配置式編程。java
問題描述以下:正則表達式
假設有對象 Student, Teacher ,它們均有屬性 Id, Name, Able 。 要求:(1) 打印這些 Student, Teacher 的字段 (Id, Name) 的內容 ; (2) 打印這些 Student, Teacher 的字段 (Name, Able) 的內容。編程
直接上代碼。應該能看懂。 須要 Java1.8 才能正常運行。api
package zzz.study.function; /** * Created by shuqin on 17/3/30. */ public interface Person { String getId(); String getName(); String able(); }
類 Student :app
package zzz.study.function; /** * Created by shuqin on 17/3/30. */ public class Student implements Person { private String studentId; private String name; private String able; public Student(String studentId, String name, String able) { this.studentId = studentId; this.name = name; this.able = able; } @Override public String getId() { return studentId; } @Override public String getName() { return name; } @Override public String able() { return able; } }
類 Teacher :ide
package zzz.study.function; /** * Created by shuqin on 17/3/30. */ public class Teacher implements Person { private String teacherId; private String name; private String able; public Teacher(String teacherId, String name, String able) { this.teacherId = teacherId; this.name = name; this.able = able; } @Override public String getId() { return teacherId; } @Override public String getName() { return name; } @Override public String able() { return able; } }
字段定義配置是核心。 這裏結合了枚舉和函數式接口。這裏之因此寫成 FieldEnum(fieldName, fieldTitle, fieldValueGetMethod) 的定義方式,是爲了便於管理。一樣能夠採用兩個 Map 來實現:Map<fieldName, fieldTitle>, Map<fieldName, fieldValueGetMethod>,這樣更適用於 Java1.6 , 不過要把兩個 Map 拼起來纔是完整的字段定義視圖。 Person::getName 是方法引用,(Person p) -> p.getName() 的簡寫形式。函數
package zzz.study.function; import java.util.function.Function; /** * Created by shuqin on 17/3/30. */ public enum FieldConf { Id("Id", "編號", Person::getId), Name("Name", "姓名", Person::getName), Able("Able", "能力", Person::able); private String name; private String title; private Function<Person, String> method; FieldConf(String name, String title, Function<Person,String> method) { this.name = name; this.title = title; this.method = method; } public String getName() { return name; } public String getTitle() { return title; } public Function<Person, String> getMethod() { return method; } }
FieldConfAccompany 是 FieldConf 的伴生類, 從 Scala 的伴生對象借鑑而來,體現了 類變量、方法 與 實例變量、方法 分離的設計思想,使得二者各司其責, 都比較簡潔。ui
package zzz.study.function; import java.util.*; /** * Created by shuqin on 17/3/30. * FieldConf 的伴生對象, 從Scala借鑑而來 */ public class FieldConfAccompany { private static Map<String, FieldConf> fieldConfMap = new HashMap<String, FieldConf>(); private static List<String> allFields = new ArrayList<>(); static { for (FieldConf fc: FieldConf.values()) { fieldConfMap.put(fc.name(), fc); allFields.add(fc.getName()); } } public static FieldConf getInstance(String name) { return fieldConfMap.get(name); } public static List<String> getAllFields() { return Collections.unmodifiableList(allFields); } }
這裏使用了 java8 Stream api 。 並無什麼特別的,只是針對列表的批量流式處理,具有延遲計算特性。this
package zzz.study.function; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Created by shuqin on 17/3/30. */ public class Report { public static void main(String[] args) { report(Arrays.asList(new String[] {"Id", "Name"}), getPersons()); report(Arrays.asList(new String[] {"Name", "Able"}), getPersons()); } public static void report(List<String> fields, List<Person> persons) { String reportTitles = fields.stream().map( field -> FieldConfAccompany.getInstance(field).getTitle() ).collect(Collectors.joining(",")); List<String> rows = persons.stream().map( p -> fields.stream().map( field -> FieldConfAccompany.getInstance(field).getMethod().apply(p) ).collect(Collectors.joining(",")) ).collect(Collectors.toList()); System.out.println(reportTitles); System.out.println(String.join("\n",rows)); } private static List<Person> getPersons() { Person s1 = new Student("s1", "liming", "Study"); Person s2 = new Student("s2", "xueying", "Piano"); Person t1 = new Teacher("t1", "Mr.Q", "Swim"); Person t2 = new Teacher("t2", "Mrs.L", "Dance"); return Arrays.asList(new Person[] {s1, s2, t1, t2}); } }
輸出:scala
編號,姓名 s1,liming s2,xueying t1,Mr.Q t2,Mrs.L 姓名,能力 liming,Study xueying,Piano Mr.Q,Swim Mrs.L,Dance
一樣先定義業務類 Person, Student, Teacher 。 能夠看到 Scala 的類定義比 Java 類定義的語法形式簡潔很多,至關於語言層面實現了 lombok 的功能。
package scalastudy.extend /** * Created by shuqin on 17/4/10. */ trait Person { def getId: String def getName: String def getAble: String } class Student(studentId: String, name:String, able:String) extends Person { override def getId: String = studentId override def getName: String = name override def getAble: String = able } class Teacher(teacherId: String, name:String, able:String) extends Person { override def getId: String = teacherId override def getName: String = name override def getAble: String = able }
Scala 沒有直接支持枚舉類型,而是提供了 Enumeration 助手類。 但是這個類也不支持定義方法字段,所以,採用樣例對象來模擬枚舉功能。這裏 apply 實現了靜態構造器的功能,經過指定名稱獲取對應的樣例對象。Scala 的 Case 功能很是強大,能夠匹配常量、變量、容器結構及元素、類對象、正則表達式等各類對象,並賦值給相應的變量。
package scalastudy.extend /** * Created by shuqin on 17/4/10. * Scala 實現枚舉; 因爲 Enumeration 不支持枚舉含有方法字段,所以採用樣例對象模擬實現。 */ sealed class FieldConf(name:String, title:String, able: (Person)=>String) { def getTitle = title def getAble = able } object FieldConf { def apply(name: String): FieldConf = { name match { case "Id" => Id case "Name" => Name case "Able" => Able case _ => Unknown } } } case object Id extends FieldConf("Id", "編號", p => p.getId) case object Name extends FieldConf("Name", "姓名", p => p.getName) case object Able extends FieldConf("Able", "能力", p => p.getAble) case object Unknown extends FieldConf("Unknown", "未知", p => "")
Scala 提供了至關多的助手方法,能夠方便地實現經常使用功能,好比對列表拼接字符串。 在 Java 中就要難受地一次次編寫無聊的 new StringBuilder , append, return sb.toString 這種套話, 而在 Scala 只要使用 mkString 便可,相似於 Python 的 join 方法。 Scala 的 lambda 表達式也很簡潔,若是隻有單變量的話,沒必要顯式寫出 p => doFor(p) 的形式, 而是直接可寫成 doFor(_) 。
package scalastudy.extend /** * Created by shuqin on 17/4/10. */ object ExtendedReport extends App { launch() def launch(): Unit = { report(List("Id", "Name"), getPersons()) report(List("Name", "Able"), getPersons()) } def report(fields:List[String], persons:List[Person]):Unit = { val titles = fields.map(FieldConf(_).getTitle).mkString(",") println(titles) val rows = persons.map( p => fields.map(FieldConf(_).getAble.apply(p)).mkString(",") ).mkString("\n") println(rows) } def getPersons():List[Person] = { List(new Student("s1", "liming", "Study"), new Student("s2", "xueying", "Piano"), new Teacher("t1", "Mr.Q", "Swim"), new Teacher("t2", "Mrs.L", "Dance")) } }