Создание парадокса

Любые мысли о создании класса Paradox без изменения самого класса?

public final class Paradox {
    private final Paradox paradox;

    public Paradox(Paradox paradox) {
        this.paradox = paradox;
        if (this.paradox == null) throw new InceptionException();
    }

    private static class InceptionException extends RuntimeException {
        public InceptionException() {
            super("Paradox requires an instance of paradox to be instantiated");
        }
    }
}

Ответы

Ответ 1

Как уже указывалось в комментариях: возможно с классом sun.misc.Unsafe:

import java.lang.reflect.Constructor;

import sun.misc.Unsafe;

public class ParadoxTest
{
    public static void main(String[] args) throws Exception
    {
        Constructor<Unsafe> unsafeConstructor = 
            Unsafe.class.getDeclaredConstructor();
        unsafeConstructor.setAccessible(true);
        Unsafe unsafe = unsafeConstructor.newInstance();
        Paradox paradox = (Paradox) unsafe.allocateInstance(Paradox.class);
        System.out.println("This is paradox: "+paradox);
    }
}

В качестве альтернативы можно использовать функцию JNI AllocObject, которая также выделяет новый объект без вызова любого из конструкторов.

EDIT: Кто-то нашел другое решение - без sun.misc.Unsafe!

EDIT2: Обратите внимание, что это решение также использует собственные классы Sun, которые являются не частью публичного API, и поэтому это решение по-прежнему не переносимо и должно not будет использоваться в производственном коде!

import java.lang.reflect.Constructor;

import sun.reflect.ReflectionFactory;

public class AnotherParadoxTest
{
    public static void main(String[] args) throws Exception
    {
        ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
        Constructor<?> declaredConstructor = 
            Object.class.getDeclaredConstructor();
        Constructor<?> constructor = rf.newConstructorForSerialization(
            Paradox.class, declaredConstructor);
        Paradox paradox = (Paradox) constructor.newInstance();
        System.out.println("This is paradox: "+paradox);
    }

}

EDIT3: только для полноты и для каждого запроса в комментариях: решение на основе JNI

Как упоминалось выше, функция JNI AllocObject не вызывает конструктор, поэтому это также можно использовать для создания экземпляра такого объекта. Это вызывающий класс, который содержит собственный метод и загружает собственную библиотеку:

public class ParadoxTestWithJni
{
    static
    {
        System.loadLibrary("Paradox");
    }
    public static void main(String[] args)
    {
        Paradox paradox = createParadox();
        System.out.println("This is paradox: "+paradox);
    }

    private native static Paradox createParadox();
}

Заголовок ParadoxTestWithJni.h, созданный для этого класса с javah:

#include <jni.h>
#ifndef _Included_ParadoxTestWithJni
#define _Included_ParadoxTestWithJni
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jobject JNICALL Java_ParadoxTestWithJni_createParadox
  (JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

Соответствующая реализация... "где происходит волшебство":

#include "ParadoxTestWithJni.h"
JNIEXPORT jobject JNICALL Java_ParadoxTestWithJni_createParadox
(JNIEnv *env, jclass c)
{
    jclass paradoxClass = env->FindClass("Paradox");
    jobject paradox = env->AllocObject(paradoxClass);
    return paradox;
}

Ответ 2

Скажем, что класс Paradox находится в пакете test, как это (конечно, он может быть в пакете по умолчанию, но вопрос его не задает):

package test;

public final class Paradox {
    private final Paradox paradox;

    public Paradox(Paradox paradox) {
        this.paradox = paradox;
        if (this.paradox == null) throw new InceptionException();
    }

    private static class InceptionException extends RuntimeException {
        public InceptionException() {
            super("Paradox requires an instance of paradox to be instantiated");
        }
    }
}

Представление XML, если оно было сериализовано XStream, класс будет выглядеть следующим образом:

<test.Paradox/>

Учитывая этот XML, его можно десериализовать без вызова конструктора:

public static void main(String[] args) {
    XStream xstream = new XStream(new DomDriver());
    Paradox paradoxFromXml = (Paradox) xstream.fromXML("<test.Paradox/>");
    System.out.println(paradoxFromXml);
}

Вывод:

[email protected]

В этом ответе используется тот факт, что XStream не вызывает конструктор во время десериализации, как указано в их FAQ:

XStream не вызывает конструктор по умолчанию во время десериализации.
Это, по сути, тот же случай, что и выше. XStream использует тот же механизм, что и сериализация JDK. При использовании расширенного режима с оптимизированным API отражения он не вызывает конструктор по умолчанию. Решением является реализация readResolve или readObject, как показано с последним вопросом.

Итак, используя прямое представление XML XStream по умолчанию, класс может быть инициализирован посредством десериализации, несмотря на то, что класс не сериализуем.

В качестве дополнительного объяснения этому XStream в этом случае, вероятно, инициализирует класс, используя SunLimitedUnsafeReflectionProvider или SunUnsafeReflectionProvider, который внутренне использует sun.misc.Unsafe, поскольку их состояния Javadoc (благодаря marco13 для указав это):

SunLimitedUnsafeReflectionProvider:

/**
 * Instantiates a new object bypassing the constructor using undocumented internal JDK features.
 * <p>
 * The code in the constructor will never be executed and parameters do not have to be known. This is the same method
 * used by the internals of standard Java serialization, but relies on internal code (sun.misc.Unsafe) that may not be
 * present on all JVMs.
 * <p>
 * <p>
 * The implementation will use standard Java functionality to write any fields. This requires Java 5 as minimum runtime
 * and is used as fallback on platforms that do not provide the complete implementation level for the internals (like
 * Dalvik).
 * <p>
 * 
 * @author J&ouml;rg Schaible
 * @author Joe Walnes
 * @author Brian Slesinsky
 * @since 1.4.7
 */

SunUnsafeReflectionProvider

/**
 * Instantiates a new object bypassing the constructor using undocumented internal JDK features.
 * <p>
 * The code in the constructor will never be executed and parameters do not have to be known. This is the same method
 * used by the internals of standard Java serialization, but relies on internal code (sun.misc.Unsafe) that may not be
 * present on all JVMs.
 * <p>
 * <p>
 * The implementation will use the same internals to write into fields. This is a lot faster and was additionally the
 * only possibility to set final fields prior to Java 5.
 * <p>
 *
 * @author Joe Walnes
 * @author Brian Slesinsky
 * @author J&ouml;rg Schaible
 * @since 1.4.7
 */

Честно говоря, я просто помнил, что XStream имел возможности инициализировать объекты без вызова конструкторов, не имея времени в момент ответа на вопрос, чтобы вникнуть в него.