ブログ

雑記

リツイートによる著作権侵害を認めた最高裁判決に思う

 Twitter社に対して,写真家がリツイートした者の発信者情報の開示を請求した裁判(事件番号:最高裁判所平成30年(受)第1412号)で,最高裁判所第三小法廷は,Twitter社の上告を棄却し,発信者情報開示を認める判決を出した。写真家が記していた著作権表示がTwitterのシステムの仕様によりリツイートされた際にトリミングされて表示を見ることができなくなってしまったことが,著作者人格権(著作者名表示権)を侵害したと判断したのである。

 私は最高裁判所のこの判断は妥当と考える。たとえシステム上の仕様であったとしても,利用上の責任はユーザが負うべきものである。インターネットという誰でも閲覧できる環境へ,他人の著作物を掲載することには大きな責任が伴うことを自覚する必要がある。判決文でも「リツイートを行うに際して,当該画像の出所や著作者名の表示,著作者の同意等に関する確認を経る負担や,権利侵害のリスクに対する心理的負担が一定程度生ずることは否定できないところ」と述べている。その程度のことはしっかり考えて投稿しなさい,ということであろう。

 林道晴裁判官が反対意見を述べているが,リツイートにあたって著作権侵害を考慮しなくてもよいという主張ではない。著作権を侵害したのはリツイートした者ではなく,最初のツイートを行った者であり,システムの仕様を決めたのはTwitter社だから,リツイート者は著作権侵害をしていないという主張である。これももっともな意見と思うが,結果的に著作権侵害を行ってしまっている以上,発信者情報の開示程度の責任を課されることはやむをえないと考える。

 判決はTwitterに対して,「著作者人格権の保護やツイッター利用者の負担回避という観点はもとより,社会的に重要なインフラとなった情報流通サービスの提供者の社会的責務という観点からも,上告人において,ツイッター利用者に対する周知等の適切な対応をすることが期待される」と,著作権保護を推進するよう注文をつけている。同社の誠意ある対応を期待したい。

最高裁判所判決全文

JDK

OpenJDK 14のソースコードを読む(2)

今回はjava.lang以外のパッケージにおけるJava 14から追加されたメソッドを見ていく。

java.io.PrintStreamクラスにvoid write(byte buf[]) メソッドとwriteBytes(byte buf[])メソッドが追加された。

/**
 * Writes all bytes from the specified byte array to this stream. If
 * automatic flushing is enabled then the {@code flush} method will be
 * invoked.
 *
 * <p> Note that the bytes will be written as given; to write characters
 * that will be translated according to the platform's default character
 * encoding, use the {@code print(char[])} or {@code println(char[])}
 * methods.
 *
 * @apiNote
 * Although declared to throw {@code IOException}, this method never
 * actually does so. Instead, like other methods that this class
 * overrides, it sets an internal flag which may be tested via the
 * {@link #checkError()} method. To write an array of bytes without having
 * to write a {@code catch} block for the {@code IOException}, use either
 * {@link #writeBytes(byte[] buf) writeBytes(buf)} or
 * {@link #write(byte[], int, int) write(buf, 0, buf.length)}.
 *
 * @implSpec
 * This method is equivalent to
 * {@link java.io.PrintStream#write(byte[],int,int)
 * this.write(buf, 0, buf.length)}.
 *
 * @param  buf   A byte array
 *
 * @throws IOException If an I/O error occurs.
 *
 * @see #writeBytes(byte[])
 * @see #write(byte[],int,int)
 *
 * @since 14
 */
@Override
public void write(byte buf[]) throws IOException {
    this.write(buf, 0, buf.length);
}
    /**
     * Writes all bytes from the specified byte array to this stream.
     * If automatic flushing is enabled then the {@code flush} method
     * will be invoked.
     *
     * <p> Note that the bytes will be written as given; to write characters
     * that will be translated according to the platform's default character
     * encoding, use the {@code print(char[])} or {@code println(char[])}
     * methods.
     *
     * @implSpec
     * This method is equivalent to
     * {@link #write(byte[], int, int) this.write(buf, 0, buf.length)}.
     *
     * @param  buf   A byte array
     *
     * @since 14
     */
    public void writeBytes(byte buf[]) {
        this.write(buf, 0, buf.length);
    }

このほかに,java.text.CompactNumberFormatクラスに,ユニコードコンソーシアムが定めた複数形を表現する仕様(https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules)に対応するためのメソッドが追加されている。これはドイツ語やフランス語での使用を想定しているようなので,日本語環境では利用することはなさそうだ。

/**
 * The {@code pluralRules} used in this compact number format.
 * {@code pluralRules} is a String designating plural rules which associate
 * the {@code Count} keyword, such as "{@code one}", and the
 * actual integer number. Its syntax is defined in Unicode Consortium's
 * <a href = "http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax">
 * Plural rules syntax</a>.
 * The default value is an empty string, meaning there is no plural rules.
 *
 * @serial
 * @since 14
 */
private String pluralRules = "";

    /**
     * Creates a {@code CompactNumberFormat} using the given decimal pattern,
     * decimal format symbols, compact patterns, and plural rules.
     * To obtain the instance of {@code CompactNumberFormat} with the standard
     * compact patterns for a {@code Locale}, {@code Style}, and {@code pluralRules},
     * it is recommended to use the factory methods given by
     * {@code NumberFormat} for compact number formatting. For example,
     * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}.
     *
     * @param decimalPattern a decimal pattern for general number formatting
     * @param symbols the set of symbols to be used
     * @param compactPatterns an array of
     *        <a href = "CompactNumberFormat.html#compact_number_patterns">
     *        compact number patterns</a>
     * @param pluralRules a String designating plural rules which associate
     *        the {@code Count} keyword, such as "{@code one}", and the
     *        actual integer number. Its syntax is defined in Unicode Consortium's
     *        <a href = "http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax">
     *        Plural rules syntax</a>
     * @throws NullPointerException if any of the given arguments is
     *        {@code null}
     * @throws IllegalArgumentException if the given {@code decimalPattern},
     *        the {@code compactPatterns} array contains an invalid pattern,
     *        a {@code null} appears in the array of compact patterns,
     *        or if the given {@code pluralRules} contains an invalid syntax
     * @see DecimalFormat#DecimalFormat(java.lang.String, DecimalFormatSymbols)
     * @see DecimalFormatSymbols
     * @since 14
     */
    public CompactNumberFormat(String decimalPattern,
            DecimalFormatSymbols symbols, String[] compactPatterns,
            String pluralRules) {

        Objects.requireNonNull(decimalPattern, "decimalPattern");
        Objects.requireNonNull(symbols, "symbols");
        Objects.requireNonNull(compactPatterns, "compactPatterns");
        Objects.requireNonNull(pluralRules, "pluralRules");

        this.symbols = symbols;
        // Instantiating the DecimalFormat with "0" pattern; this acts just as a
        // basic pattern; the properties (For example, prefix/suffix)
        // are later computed based on the compact number formatting process.
        decimalFormat = new DecimalFormat(SPECIAL_PATTERN, this.symbols);

        // Initializing the super class state with the decimalFormat values
        // to represent this CompactNumberFormat.
        // For setting the digits counts, use overridden setXXX methods of this
        // CompactNumberFormat, as it performs check with the max range allowed
        // for compact number formatting
        setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits());
        setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits());
        setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits());
        setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits());

        super.setGroupingUsed(decimalFormat.isGroupingUsed());
        super.setParseIntegerOnly(decimalFormat.isParseIntegerOnly());

        this.compactPatterns = compactPatterns;

        // DecimalFormat used for formatting numbers with special pattern "0".
        // Formatting is delegated to the DecimalFormat's number formatting
        // with no fraction digits
        this.decimalPattern = decimalPattern;
        defaultDecimalFormat = new DecimalFormat(this.decimalPattern,
                this.symbols);
        defaultDecimalFormat.setMaximumFractionDigits(0);

        this.pluralRules = pluralRules;

        // Process compact patterns to extract the prefixes, suffixes and
        // divisors
        processCompactPatterns();
    }
JDK

OpenJDK 14のソースコードを読む(1)

OpenJDK 14のソースコードの中から”@since 14″を検索して,追加されたところを見てみる。deprecatedとされた部分も検索されてしまうが,こちらは一旦無視して,追加された部分に注目する。

java.lang.Classクラス

java.lang.Classクラスにはrecordの追加に伴うメソッドが追加されている。

@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
                             essentialAPI=false)
@SuppressWarnings("preview")
@CallerSensitive
public RecordComponent[] getRecordComponents() {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
    }
    if (!isRecord()) {
        return null;
    }
    RecordComponent[] recordComponents = getRecordComponents0();
    if (recordComponents == null) {
        return new RecordComponent[0];
    }
    return recordComponents;
}
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
                             essentialAPI=false)
public boolean isRecord() {
    return getSuperclass() == JAVA_LANG_RECORD_CLASS && isRecord0();
}

java.lang.Recordクラス

新たに追加された主要概念の1つであるRecordを表すクラスである。抽象クラスとなっている。

/*
 * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.lang;

/**
 * {@preview Associated with records, a preview feature of the Java language.
 *
 *           This class is associated with <i>records</i>, a preview
 *           feature of the Java language. Programs can only use this
 *           class when preview features are enabled. Preview features
 *           may be removed in a future release, or upgraded to permanent
 *           features of the Java language.}
 *
 * This is the common base class of all Java language record classes.
 *
 * <p>More information about records, including descriptions of the
 * implicitly declared methods synthesized by the compiler, can be
 * found in section 8.10 of
 * <cite>The Java™ Language Specification</cite>.
 *
 * <p>A <em>record class</em> is a shallowly immutable, transparent carrier for
 * a fixed set of values, called the <em>record components</em>.  The Java™
 * language provides concise syntax for declaring record classes, whereby the
 * record components are declared in the record header.  The list of record
 * components declared in the record header form the <em>record descriptor</em>.
 *
 * <p>A record class has the following mandated members: a public <em>canonical
 * constructor</em>, whose descriptor is the same as the record descriptor;
 * a private final field corresponding to each component, whose name and
 * type are the same as that of the component; a public accessor method
 * corresponding to each component, whose name and return type are the same as
 * that of the component.  If not explicitly declared in the body of the record,
 * implicit implementations for these members are provided.
 *
 * <p>The implicit declaration of the canonical constructor initializes the
 * component fields from the corresponding constructor arguments.  The implicit
 * declaration of the accessor methods returns the value of the corresponding
 * component field.  The implicit declaration of the {@link Object#equals(Object)},
 * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived
 * from all of the component fields.
 *
 * <p>The primary reasons to provide an explicit declaration for the
 * canonical constructor or accessor methods are to validate constructor
 * arguments, perform defensive copies on mutable components, or normalize groups
 * of components (such as reducing a rational number to lowest terms.)
 *
 * <p>For all record classes, the following invariant must hold: if a record R's
 * components are {@code c1, c2, ... cn}, then if a record instance is copied
 * as follows:
 * <pre>
 *     R copy = new R(r.c1(), r.c2(), ..., r.cn());
 * </pre>
 * then it must be the case that {@code r.equals(copy)}.
 *
 * @apiNote
 * A record class that {@code implements} {@link java.io.Serializable} is said
 * to be a <i>serializable record</i>. Serializable records are serialized and
 * deserialized differently than ordinary serializable objects. During
 * deserialization the record's canonical constructor is invoked to construct
 * the record object. Certain serialization-related methods, such as readObject
 * and writeObject, are ignored for serializable records. More information about
 * serializable records can be found in
 * <a href="{@docRoot}/java.base/java/io/ObjectInputStream.html#record-serialization">record serialization</a>.
 *
 * @jls 8.10 Record Types
 * @since 14
 */
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
                             essentialAPI=true)
public abstract class Record {
    /**
     * Constructor for record classes to call.
     */
    protected Record() {}

    /**
     * Indicates whether some other object is "equal to" this one.  In addition
     * to the general contract of {@link Object#equals(Object) Object.equals},
     * record classes must further obey the invariant that when
     * a record instance is "copied" by passing the result of the record component
     * accessor methods to the canonical constructor, as follows:
     * <pre>
     *     R copy = new R(r.c1(), r.c2(), ..., r.cn());
     * </pre>
     * then it must be the case that {@code r.equals(copy)}.
     *
     * @implSpec
     * The implicitly provided implementation returns {@code true} if
     * and only if the argument is an instance of the same record type
     * as this object, and each component of this record is equal to
     * the corresponding component of the argument; otherwise, {@code
     * false} is returned. Equality of a component {@code c} is
     * determined as follows:
     * <ul>
     *
     * <li> If the component is of a reference type, the component is
     * considered equal if and only if {@link
     * java.util.Objects#equals(Object,Object)
     * Objects.equals(this.c(), r.c()} would return {@code true}.
     *
     * <li> If the component is of a primitive type, using the
     * corresponding primitive wrapper class {@code PW} (the
     * corresponding wrapper class for {@code int} is {@code
     * java.lang.Integer}, and so on), the component is considered
     * equal if and only if {@code
     * PW.valueOf(this.c()).equals(PW.valueOf(r.c()))} would return
     * {@code true}.
     *
     * </ul>
     *
     * The implicitly provided implementation conforms to the
     * semantics described above; the implementation may or may not
     * accomplish this by using calls to the particular methods
     * listed.
     *
     * @see java.util.Objects#equals(Object,Object)
     *
     * @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is equal to the
     *          argument; {@code false} otherwise.
     */
    @Override
    public abstract boolean equals(Object obj);

    /**
     * Obeys the general contract of {@link Object#hashCode Object.hashCode}.
     *
     * @implSpec
     * The implicitly provided implementation returns a hash code value derived
     * by combining the hash code value for all the components, according to
     * {@link Object#hashCode()} for components whose types are reference types,
     * or the primitive wrapper hash code for components whose types are primitive
     * types.
     *
     * @see     Object#hashCode()
     *
     * @return  a hash code value for this object.
     */
    @Override
    public abstract int hashCode();

    /**
     * Obeys the general contract of {@link Object#toString Object.toString}.
     *
     * @implSpec
     * The implicitly provided implementation returns a string that is derived
     * from the name of the record class and the names and string representations
     * of all the components, according to {@link Object#toString()} for components
     * whose types are reference types, and the primitive wrapper {@code toString}
     * method for components whose types are primitive types.
     *
     * @see     Object#toString()
     *
     * @return  a string representation of the object.
     */
    @Override
    public abstract String toString();
}

java.lang.ElementType列挙子

ElementTypeのenumに新しくRECORD_COMPONENTが追加された。レコードに対応したものと思われる。

 * Record component
 *
 * @jls 8.10.3 Record Members
 * @jls 9.7.4 Where Annotations May Appear
 *
 * @since 14
 */
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
                             essentialAPI=true)
RECORD_COMPONENT;

java.lang.RecordComponentクラス

レコードを表すキモとなるクラスである。

/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.lang.reflect;

import jdk.internal.access.SharedSecrets;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.TypeAnnotation;
import sun.reflect.annotation.TypeAnnotationParser;
import sun.reflect.generics.factory.CoreReflectionFactory;
import sun.reflect.generics.factory.GenericsFactory;
import sun.reflect.generics.repository.FieldRepository;
import sun.reflect.generics.scope.ClassScope;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;

/**
 * {@preview Associated with records, a preview feature of the Java language.
 *
 *           This class is associated with <i>records</i>, a preview
 *           feature of the Java language. Preview features
 *           may be removed in a future release, or upgraded to permanent
 *           features of the Java language.}
 *
 * A {@code RecordComponent} provides information about, and dynamic access to, a
 * component of a record class.
 *
 * @see Class#getRecordComponents()
 * @see java.lang.Record
 * @jls 8.10 Record Types
 * @since 14
 */
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
                             essentialAPI=false)
public final class RecordComponent implements AnnotatedElement {
    // declaring class
    private Class<?> clazz;
    private String name;
    private Class<?> type;
    private Method accessor;
    private String signature;
    // generic info repository; lazily initialized
    private transient FieldRepository genericInfo;
    private byte[] annotations;
    private byte[] typeAnnotations;
    @SuppressWarnings("preview")
    private RecordComponent root;

    // only the JVM can create record components
    private RecordComponent() {}

    /**
     * Returns the name of this record component.
     *
     * @return the name of this record component
     */
    public String getName() {
        return name;
    }

    /**
     * Returns a {@code Class} that identifies the declared type for this
     * record component.
     *
     * @return a {@code Class} identifying the declared type of the component
     * represented by this record component
     */
    public Class<?> getType() {
        return type;
    }

    /**
     * Returns a {@code String} that describes the  generic type signature for
     * this record component.
     *
     * @return a {@code String} that describes the generic type signature for
     * this record component
     *
     * @jvms 4.7.9.1 Signatures
     */
    public String getGenericSignature() {
        return signature;
    }

    /**
     * Returns a {@code Type} object that represents the declared type for
     * this record component.
     *
     * <p>If the declared type of the record component is a parameterized type,
     * the {@code Type} object returned reflects the actual type arguments used
     * in the source code.
     *
     * <p>If the type of the underlying record component is a type variable or a
     * parameterized type, it is created. Otherwise, it is resolved.
     *
     * @return a {@code Type} object that represents the declared type for
     *         this record component
     * @throws GenericSignatureFormatError if the generic record component
     *         signature does not conform to the format specified in
     *         <cite>The Java™ Virtual Machine Specification</cite>
     * @throws TypeNotPresentException if the generic type
     *         signature of the underlying record component refers to a non-existent
     *         type declaration
     * @throws MalformedParameterizedTypeException if the generic
     *         signature of the underlying record component refers to a parameterized
     *         type that cannot be instantiated for any reason
     */
    public Type getGenericType() {
        if (getGenericSignature() != null)
            return getGenericInfo().getGenericType();
        else
            return getType();
    }

    // Accessor for generic info repository
    private FieldRepository getGenericInfo() {
        // lazily initialize repository if necessary
        if (genericInfo == null) {
            // create and cache generic info repository
            genericInfo = FieldRepository.make(getGenericSignature(), getFactory());
        }
        return genericInfo; //return cached repository
    }

    // Accessor for factory
    private GenericsFactory getFactory() {
        Class<?> c = getDeclaringRecord();
        // create scope and factory
        return CoreReflectionFactory.make(c, ClassScope.make(c));
    }

    /**
     * Returns an {@code AnnotatedType} object that represents the use of a type to specify
     * the declared type of this record component.
     *
     * @return an object representing the declared type of this record component
     */
    public AnnotatedType getAnnotatedType() {
        return TypeAnnotationParser.buildAnnotatedType(typeAnnotations,
                SharedSecrets.getJavaLangAccess().
                        getConstantPool(getDeclaringRecord()),
                this,
                getDeclaringRecord(),
                getGenericType(),
                TypeAnnotation.TypeAnnotationTarget.FIELD);
    }

    /**
     * Returns a {@code Method} that represents the accessor for this record
     * component.
     *
     * @return a {@code Method} that represents the accessor for this record
     * component
     */
    public Method getAccessor() {
        return accessor;
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     */
    @Override
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        Objects.requireNonNull(annotationClass);
        return annotationClass.cast(declaredAnnotations().get(annotationClass));
    }

    private transient volatile Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

    private Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
        Map<Class<? extends Annotation>, Annotation> declAnnos;
        if ((declAnnos = declaredAnnotations) == null) {
            synchronized (this) {
                if ((declAnnos = declaredAnnotations) == null) {
                    @SuppressWarnings("preview")
                    RecordComponent root = this.root;
                    if (root != null) {
                        declAnnos = root.declaredAnnotations();
                    } else {
                        declAnnos = AnnotationParser.parseAnnotations(
                                annotations,
                                SharedSecrets.getJavaLangAccess()
                                        .getConstantPool(getDeclaringRecord()),
                                getDeclaringRecord());
                    }
                    declaredAnnotations = declAnnos;
                }
            }
        }
        return declAnnos;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Annotation[] getAnnotations() {
        return getDeclaredAnnotations();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Annotation[] getDeclaredAnnotations() { return AnnotationParser.toArray(declaredAnnotations()); }

    /**
     * Returns a string describing this record component. The format is
     * the record component type, followed by a space, followed by the name
     * of the record component.
     * For example:
     * <pre>
     *    String name
     *    int age
     * </pre>
     *
     * @return a string describing this record component
     */
    public String toString() {
        return (getType().getTypeName() + " " + getName());
    }

    /**
     * Returns the record class which declares this record component.
     *
     * @return The record class declaring this record component.
     */
    public Class<?> getDeclaringRecord() {
        return clazz;
    }
}

JDK

Java 14 新機能 (5)

JPackageというツールが導入されてインストーラパッケージを作成できるようになっていますが,私のPC(Windows)ではインストーラを作成するツールであるWixとの連携がうまくいかず,作成できませんでした。JPackageは裏でWixのcandle.exeとlight.exeを呼び出しているようで,candle.exeは正常に終了しましたが,light.exeは対応していないコードページを使おうとして動かなくなってしまったようです。

これまで通り手動でjarファイルにパッケージングして,Wixを動かせばよいだけですが,日本語環境までは十分に配慮されていない感じでした。

JDK

Java 14 新機能 (4)

次はrecordを紹介する。recordは簡易版のclassのようなものである。POJOで書いていた部分はrecordを使うことが可能かもしれない。recordは他のクラスを継承できず,private finalなインスタンス変数しか持つことができない。recordは暗黙的にfinalである。

public class RecordExample
{
    public static void main(String[] args)
    {
        Person person = new Person("はらだ たかひこ", 38);
        System.out.printf("名前: %s\n", person.getName());
        System.out.printf("年齢: %d歳\n", person.getAge());
    }
}
record Person(String name, int age)
{
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
}
JDK

Java 14 新機能 (3)

3番目に新たなswitchステートメントの記述方法を紹介する。switchはC言語の仕様から導入されてきたこともあり,しばしばbreakが抜けてトラブルを起こすことがあった。今回,”->”を用いた新たな記法が導入されている。

public class SwitchStatement
{
    public static void main(String[] args)
    {
        int i = 1;
        switch(i)
        {
            case 0 -> System.out.println(“ZERO”);
            case 1 -> System.out.println(“ONE”);
            case 2 -> System.out.println(“TWO”);
        }
    }
}
未分類

Java 14 新機能(2)

次は進化したinstanceof演算子を試してみる。従来のinstanceof演算子はオブジェクトが指定したクラスのインスタンスかどうかをチェックし,結果をboolean型で返すだけのものであった。進化したinstanceof演算子では,オブジェクトが指定した型に合致する場合はキャストして変数に格納するところまでやってくれる。

public class Instanceof
{
    public static void main(String[] args)
    {
        Object i = 1;
        if(i instanceof Integer integer)
        {
            // 従来は(Integer)i + 2と明示的なキャストをしていた
            System.out.println(integer + 2);
        }
    }
}