親Class を全て参照する

package sample 配下に

public class Foo extends FooBase{
}

public class FooBase extends AppBase{
}

public class AppBase{
}

とあるとき、親Class 全てを求める方法

UnaryOperator<Class<?>> superFind = c->c.getSuperclass();

Class<?> cl = Foo.class;
while(!(cl=superFind.apply(cl)).equals(Object.class)) {
	System.out.println(cl.getName());
}

あるいは、

UnaryOperator<Class<?>> superFind = c->c.getSuperclass();
for(Class<?> cl = Foo.class; !(cl=superFind.apply(cl)).equals(Object.class);){
	System.out.println(cl.getName());
}

自Class を含めてStream にする

static Stream<Class<?>> superStream(Class<?> cl){
	UnaryOperator<Class<?>> superFind = c->c.getSuperclass();
	Builder<Class<?>> builder = Stream.builder();
	builder.add(cl);
	Class<?> c = cl;
	while(!(c=superFind.apply(c)).equals(Object.class)) {
		builder.add(c);
	}
	return builder.build();
}
superStream(Foo.class).map(Class::getName).forEach(System.out::println);

なんか、もっと良い方法がないものだろうか。。。

Collector.of のサンプル(2)

フィールド名、及び getter / setter が同じ Java Bean のコピーも、Collector.of のサンプルとして
以下のように書ける。

public static<T,U> U fieldcopy(T t, U u){
   UnaryOperator<String> topUpper = s->s.substring(0, 1).toUpperCase() + s.substring(1);
   return Arrays.stream(t.getClass().getDeclaredFields()).map(f->f.getName()).collect(
      Collector.of(()->u, (r, n)->{
         try{
            Method getter = t.getClass().getDeclaredMethod(
               (t.getClass().getDeclaredField(n).getType().equals(boolean.class) ? "is" : "get")
                + topUpper.apply(n)
            );
            Method setter = r.getClass().getDeclaredMethod(
               "set"+ topUpper.apply(n), getter.getReturnType()
            );
            setter.invoke(r, getter.invoke(t));
         }catch(SecurityException | NoSuchFieldException | NoSuchMethodException 
                | IllegalAccessException | IllegalArgumentException | InvocationTargetException e){
            throw new RuntimeException(e);
         }
      }, (a, b)->a, a->a)
   );
}

これは、完全に同じフィールド、同じ getter / setter メソッドがあるのが前提である。
以前、yipuran-coreFieldUtil なるものを作り、
yipuran-core/FieldUtil.java at master · yipuran/yipuran-core · GitHub
 public static <R, T> R copy(T t, Supplier<R> s)
 public static <R, T> R copylenient(T t, Supplier<R> s)

というメソッドを作ったが、
フィールド名、及び getter / setter が同じという約束なら、上の Collector.of の方が良いような気がしてきた。
継承 extends がある場合は、別途工夫が必要ではある。

Collector.of のサンプル(1)

java.util.stream.Collector#of の簡単なサンプル

Collectors.toSet() で済む話ではあるのだが、
static <T,A,R> Collector<T,A,R> of(Supplier<A> supplier,
BiConsumer<A,T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Collector.Characteristics... characteristics)

をしっかりと理解するのには、そこそこ良いサンプル

stream の集計として HashSet に格納して全て同一(equalsメソッド)要素であるかをチェック

public static <T> Collector<T, Set<T>, Boolean> isSingle(){
   return Collector.of(HashSet::new, Set::add,
      (a1, a2)->{
         a1.addAll(a2);
         return a1;
      },
      a->a.size()==1
   );
}

stream の集計として HashSet に格納してそのサイズを求める。

static <T> Collector<T, Set<T>, Integer> hashsetsize(){
   return Collector.of(HashSet::new, Set::add,
      (a1, a2)->{
         a1.addAll(a2);
         return a1;
      },
      a->a.size()
   );
}

いずれも、Collectors.toSet()の結果で事足りるのだが、Stream#collect の戻り値として欲しい場合は
良いかもしれない。

Stream の Collector 見直し。。

去年、書いたStream の終端処理 Collector 以下は、
リストから重複要素を抽出する。 - Oboe吹きプログラマの黙示録
並行Stream実行ではNGであるのを反省して、以下のように修正すべきです。

static <T> Collector<T, Map<T, Integer>, List<T>> duplicatedList(){
   return Collector.of(HashMap::new,
      (r, t)->r.put(t, Optional.ofNullable(r.get(t)).map(i->i+1).orElse(1)),
      (a1, a2)->{
         a1.entrySet().stream().forEach(e->{
            a2.put(e.getKey(), Optional.ofNullable(a2.get(e.getKey()))
                     .map(i->i+e.getValue()).orElse(e.getValue()));
         });
         return a2;
      },
      a->a.entrySet().stream().filter(e->e.getValue() > 1)
               .map(e->e.getKey())
               .collect(Collectors.toList()),
      Characteristics.CONCURRENT
   );
}

java.util.stream.Collector.Characteristics の指定

CONCURRENT マルチスレッドで呼ばれても大丈夫な場合に指定する。
UNORDERED 順序を保証しない場合に指定する。
IDENTITY_FINISH finisherが省略可能

メソッド名、duplicatedList というのは、あまり良い名称に思えませんが、

重複カウント数を指定するなら以下です。

static <T> Collector<T, Map<T, Integer>, List<T>> duplicateCountOverList(int cnt){
   return Collector.of(HashMap::new,
      (r, t)->r.put(t, Optional.ofNullable(r.get(t)).map(i->i+1).orElse(1)),
      (a1, a2)->{
         a1.entrySet().stream().forEach(e->{
            a2.put(e.getKey(), Optional.ofNullable(a2.get(e.getKey()))
                     .map(i->i+e.getValue()).orElse(e.getValue()));
         });
         return a2;
      },
      a->a.entrySet().stream().filter(e->e.getValue() > cnt)
               .map(e->e.getKey())
               .collect(Collectors.toList()),
      Characteristics.CONCURRENT
   );
}

しかし、やはり以下のように  Collectors.groupingBy を使って書くことの方が多いかな?

List<String> res =
list.stream().collect(Collectors.groupingBy(t->t, Collectors.counting()))
.entrySet().stream().filter(e->e.getValue() == 2)
.map(e->e.getKey())
.collect(Collectors.toList());

SpringBoot で gson使用

Jackson も、Google gsonspring-boot-starter-json で利用するための構成があります。
SpringBoot で、gson だけに使用を絞りたい時はどうすればいいのでしょう。

まずは、Gsonを使うための設定
for Gradle build.gradle

dependencies {
    compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
}

for Maven pom.xml

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

application.properties

# aaa
spring.http.converters.preferred-json-mapper=gson

その他、application.properties に設定できる属性

# Format to use when serializing Date objects.
spring.gson.date-format= yyyy-MM-dd HH:mm:ss
 
# Whether to disable the escaping of HTML characters such as '<', '>', etc.
spring.gson.disable-html-escaping= true
 
# Whether to exclude inner classes during serialization.
spring.gson.disable-inner-class-serialization= 
 
# Whether to enable serialization of complex map keys (i.e. non-primitives).
spring.gson.enable-complex-map-key-serialization= 
 
# Whether to exclude all fields from consideration for serialization or deserialization that do not have the "Expose" annotation.
spring.gson.exclude-fields-without-expose-annotation= 
 
# Naming policy that should be applied to an object's field during serialization and deserialization.
spring.gson.field-naming-policy= 
 
# Whether to generate non executable JSON by prefixing the output with some special text.
spring.gson.generate-non-executable-json= 
 
# Whether to be lenient about parsing JSON that doesn't conform to RFC 4627.
spring.gson.lenient= 
 
# Serialization policy for Long and long types.
spring.gson.long-serialization-policy= 
 
# Whether to output serialized JSON that fits in a page for pretty printing.
spring.gson.pretty-printing= 
 
# Whether to serialize null fields.
spring.gson.serialize-nulls= 

これで、Autowireも使えるわけです。

@Autowire
private Gson gson;

Jasckson 使用を除外するためには以下が必要です
project dependencies 除外

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </exclusion>
    </exclusions>
</dependency>

SpringBootApplication.java

@SpringBootApplication(exclude = {JacksonAutoConfiguration.class})
public class SpringBootApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootApplication.class, args);
    }
}

Jackson 未定義フィールドのJSON読込み、JSONコメント

JSON にコメントを書く場合、いろいろあって
よく紹介されてるのが、、
Hjson ( .hjson )
https://hjson.github.io/

JSON5 ( .json5o )
https://json5.org/

JSONC ( .jsonc )
https://github.com/microsoft/node-jsonc-parser
Visual Studio Code 向け、

ですが、使用条件、環境に制約が生まれるなら、やはり、JSON書式ルールに
従う中でコメントを書くのが一番良い気がします。
(読み側がそのフィールドを無視すれば良いだけのこと)

{
    "coment" :  "#############################",
    "coment" :  "    コメント                  ",
    "coment" :  "#############################",
    "id": 10012
}

Jackson では、コメントに対するフィールド名が未定義で
このまま読むと、UnrecognizedPropertyException を起こします。

  com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field
が起きるはずです。
Google gson で読込む場合は、未定義のJSONキー、フィールド名があろうが、
特に何もしなくても、無視してくれます。(そういうところが gson は嬉しいです。)

Jackson で未定義フィールドのある JSON を読み込む時の対応は2通りです。
@JsonIgnoreProperties を付ける
解析、マッピングされるクラスのアノテーションを付けて読込ます。

@JsonIgnoreProperties(ignoreUnknown=true)
public class Sample {

ObjectMapper で設定

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);