Jak uzyskać instancję klasy typu generic T?

Mam zajęcia z generyki, Foo<T>. W metodzie Foo chcę uzyskać instancję klasy typu T, ale po prostu nie mogę wywołać T.class.

Jaki jest preferowany sposób obejścia go za pomocą T.class?

 744
Author: Michael, 2010-08-09

22 answers

Krótka odpowiedź jest taka, że w Javie nie ma sposobu na określenie typu runtime ogólnych parametrów typu. Sugeruję przeczytanie rozdziału o usuwaniu typów w Java Tutorial Po Więcej Szczegółów.

Popularnym rozwiązaniem jest przekazanie Class parametru type do konstruktora typu generycznego, np.

class Foo<T> {
    final Class<T> typeParameterClass;

    public Foo(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public void bar() {
        // you can access the typeParameterClass here and do whatever you like
    }
}
 608
Author: Zsolt Török,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-09 07:09:48

Szukałem sposobu, aby to zrobić sam bez dodawania dodatkowej zależności do ścieżki klasowej. Po jakimś śledztwie okazało się, że na możliwe, o ile masz ogólny supertyp. Było to dla mnie OK, ponieważ pracowałem z Dao warstwą z ogólnym supertyp warstwy. Jeśli to pasuje do twojego scenariusza, to jest to najbardziej schludne podejście IMHO.

Większość przypadków użycia generyków, z którymi się spotkałem, ma jakiś rodzaj ogólnego supertypu, np. List<T> dla ArrayList<T> lub GenericDAO<T> dla DAO<T>, itd.

Pure Java solution

Artykuł uzyskiwanie dostępu do typów generycznych podczas pracy w Javie wyjaśnia, jak można to zrobić za pomocą czystej Javy.

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

Rozwiązanie sprężyny

Mój projekt używał Spring co jest jeszcze lepsze, ponieważ Spring ma przydatną metodę znajdowania typu. Jest to dla mnie najlepsze podejście, ponieważ wygląda schludnie. Myślę, że gdybyś nie używał Springa, mógłbyś napisać własną metodę użytkową.

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }
 254
Author: Ben Thurley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-26 14:39:40

Istnieje jednak mała luka: jeśli zdefiniujesz klasę Foo jako abstrakcyjną. Oznacza to, że musisz utworzyć klasę jako:

Foo<MyType> myFoo = new Foo<MyType>(){};

(zwróć uwagę na podwójne szelki na końcu.)

Teraz możesz pobrać typ T w czasie wykonywania:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

Zauważ jednak, że mySuperclass musi być superklasą definicji klasy faktycznie definiującej ostateczny typ dla T.

Nie jest też zbyt elegancki, ale musisz zdecydować, czy wolisz new Foo<MyType>(){} czy new Foo<MyType>(MyType.class); w Twoim kodzie.


Na przykład:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

Potem:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}
 107
Author: bert bruynooghe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-07 11:37:59

Standardowym podejściem / obejściem / rozwiązaniem jest dodanie class obiektu do konstruktora(ów), np.:

 public class Foo<T> {

    private Class<T> type;
    public Foo(Class<T> type) {
      this.type = type;
    }

    public Class<T> getType() {
      return type;
    }

    public T newInstance() {
      return type.newInstance();
    }
 }
 40
Author: Andreas Dolk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 17:37:20

Wyobraź sobie, że masz abstrakcyjną superklasę, która jest ogólna:

public abstract class Foo<? extends T> {}

I wtedy masz drugą klasę, która rozszerza Foo z ogólnym paskiem, który rozszerza T:

public class Second extends Foo<Bar> {}

Możesz uzyskać klasę Bar.class w klasie Foo, wybierając Type (od odpowiedzi Berta bruynooghe) i wnioskując ją za pomocą instancji Class:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

Należy zauważyć, że ta operacja nie jest idealna, więc dobrym pomysłem jest buforowanie obliczonej wartości, aby uniknąć wielokrotnych obliczeń na tej podstawie. Jednym z typowych zastosowań jest w ogólnej implementacji DAO.

Ostateczna realizacja:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

Zwracana wartość to Bar.Klasa wywołana z klasy Foo w innej funkcji lub z klasy Bar.

 21
Author: droidpl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-24 10:32:07

Oto rozwiązanie robocze:

@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
    try {
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
        Class<?> clazz = Class.forName(className);
        return (Class<T>) clazz;
    } catch (Exception e) {
        throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
    }
} 

Przypisy: może być używany tylko jako superclass

  1. musi być rozszerzona o klasę typed(Child extends Generic<Integer>)

lub

  1. musi być utworzona jako implementacja anonimowa (new Generic<Integer>() {};)
 18
Author: Yaroslav Kovbas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-17 08:55:59

Miałem ten problem w abstrakcyjnej klasie generycznej. W tym konkretnym przypadku rozwiązanie jest prostsze:

abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

I Później Klasa pochodna:

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}
 11
Author: user1154664,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-05 23:36:38

Nie możesz tego zrobić z powodu typu kasowania. Zobacz także pytanie o przepełnienie stosuJava generics - type erasure-kiedy i co się dzieje.

 10
Author: Konrad Garus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:30

Lepszą drogą niż klasa, którą sugerują inni, jest przejście do obiektu, który może zrobić to, co zrobiłbyś z klasą, np. utworzyć nową instancję.

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
 9
Author: Ricky Clarkson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 13:04:00

Mam (brzydkie, ale skuteczne) rozwiązanie tego problemu, z którego ostatnio korzystałem:

import java.lang.reflect.TypeVariable;


public static <T> Class<T> getGenericClass()
{
    __<T> ins = new __<T>();
    TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 

    return (Class<T>)cls[0].getClass();
}

private final class __<T> // generic helper class which does only provide type information
{
    private __()
    {
    }
}
 6
Author: unknown6656,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-07 06:20:34

To możliwe:

class Foo<T> {
  Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}

Potrzebujesz dwóch funkcji z Hibernate-generic-Dao/blob/master/dao/src/main/java/com/googlecode/genericdao/dao/DAOUtil.java .

Aby uzyskać więcej wyjaśnień, zobacz refleksyjne leki generyczne.

 5
Author: Peter Tseng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-09 15:15:03

Zakładam, że skoro masz klasę generyczną, to masz taką zmienną:

private T t;

(ta zmienna musi przyjąć wartość w konstruktorze)

W takim przypadku można po prostu utworzyć następującą metodę:

Class<T> getClassOfInstance()
{
    return (Class<T>) t.getClass();
}
Mam nadzieję, że to pomoże!
 4
Author: Pontios,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-22 11:32:28

Znalazłem na to ogólny i prosty sposób. W mojej klasie stworzyłem metodę, która zwraca typ generyczny zgodnie z jego pozycją w definicji klasy. Przyjmijmy taką definicję klasy:

public class MyClass<A, B, C> {

}

Teraz stwórzmy kilka atrybutów do utrzymywania typów:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

Następnie można utworzyć metodę generyczną, która zwraca typ na podstawie indeksu definicji generycznej:

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

Na koniec, w konstruktorze wystarczy wywołać metodę i wysłać indeks dla każdego Typ. Pełny kod powinien wyglądać następująco:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}
 3
Author: Juan Urrego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-03 14:21:01

Jak wyjaśniono w innych odpowiedziach, aby użyć tego podejścia ParameterizedType, musisz rozszerzyć klasę, ale wydaje się to dodatkową pracą, aby stworzyć zupełnie nową klasę, która ją rozszerza...

Więc, uczynienie klasy abstrakcyjną zmusza cię do jej rozszerzenia, spełniając w ten sposób wymóg podklasowania. (za pomocą lombok ' s @Getter).

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

    public ConfigurationDefinition(...) {
        this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        ...
    }
}

Teraz rozszerzyć go bez definiowania nowej klasy. (Zwróć uwagę na {} na końcu... rozszerzone, ale nie nadpisuj niczego-chyba, że chcesz).

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();
 2
Author: Riaan Schutte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-20 03:01:24

To całkiem proste. Jeśli potrzebujesz z tej samej klasy:

Class clazz = this.getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
try {
        Class typeClass = Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName() );
        // You have the instance of type 'T' in typeClass variable

        System.out.println( "Class instance name: "+  typeClass.getName() );
    } catch (ClassNotFoundException e) {
        System.out.println( "ClassNotFound!! Something wrong! "+ e.getMessage() );
    }
 2
Author: Gana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-07 12:22:31
   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }
 1
Author: Abel ANEIROS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-02 12:45:55

Jeśli rozszerzasz lub implementujesz dowolną klasę / interfejs, która używa generycznych, możesz uzyskać ogólny typ nadrzędnej klasy / interfejsu, bez modyfikowania żadnej istniejącej klasy/interfejsu.

Mogą być trzy możliwości,

Przypadek 1 Kiedy twoja klasa rozszerza klasę, która używa generyków

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

Przypadek 2 Kiedy twoja klasa implementuje interfejs, który używa Generics

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

Przypadek 3 Gdy twój interfejs jest rozszerzenie interfejsu używającego Generics

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}
 1
Author: Anil Agrawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-05 10:43:57

Właściwie, przypuszczam, że masz pole w swojej klasie typu T. Jeśli nie ma pola typu T, jaki jest sens posiadania typu ogólnego? Możesz więc po prostu zrobić instanceof na tym polu.

W moim przypadku mam

List<T> items;
w mojej klasie, a ja sprawdzam czy typ klasy to "local" by
if (items.get(0) instanceof Locality) ...

Oczywiście działa to tylko wtedy, gdy całkowita liczba możliwych klas jest ograniczona.

 0
Author: Christine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-11 11:51:32

To pytanie jest stare, ale teraz najlepsze jest użycie google Gson.

Przykład, aby uzyskać niestandardowe viewModel.

Class<CustomViewModel<String>> clazz = new GenericClass<CustomViewModel<String>>().getRawType();
CustomViewModel<String> viewModel = viewModelProvider.get(clazz);

Klasa typu generycznego

class GenericClass<T>(private val rawType: Class<*>) {

    constructor():this(`$Gson$Types`.getRawType(object : TypeToken<T>() {}.getType()))

    fun getRawType(): Class<T> {
        return rawType as Class<T>
    }
}
 0
Author: Vahe Gharibyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-14 12:05:20

I wanted to pass T.class do metody wykorzystującej Generyki

Metoda readFile odczytuje a .plik csv określony przez nazwę pliku z fullpath. Mogą być pliki csv z różną zawartością, dlatego muszę przekazać klasę pliku modelu, aby uzyskać odpowiednie obiekty. Ponieważ jest to czytanie pliku csv chciałem zrobić w sposób ogólny. Z jakiegoś powodu żadne z powyższych rozwiązań nie zadziałało dla mnie. Muszę użyć Żeby to zadziałało. Używam biblioteki opencsv do parsowanie plików CSV.

private <T>List<T> readFile(String fileName, Class<? extends T> type) {

    List<T> dataList = new ArrayList<T>();
    try {
        File file = new File(fileName);

        Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
        Reader headerReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

        CSVReader csvReader = new CSVReader(headerReader);
        // create csv bean reader
        CsvToBean<T> csvToBean = new CsvToBeanBuilder(reader)
                .withType(type)
                .withIgnoreLeadingWhiteSpace(true)
                .build();

        dataList = csvToBean.parse();
    }
    catch (Exception ex) {
        logger.error("Error: ", ex);
    }

    return dataList;
}

Tak nazywa się metoda readFile

List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);
 0
Author: Madhu Tomy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-23 21:09:17

Wiele osób nie zna tej sztuczki! Właśnie go dziś znalazłem! To działa jak sen! Zobacz ten przykład:

public static void main(String[] args) {
    Date d=new Date();  //Or anything you want!
    printMethods(i);
}

public static <T> void printMethods(T t){
    Class<T> clazz= (Class<T>) t.getClass(); // There you go!
    for ( Method m : clazz.getMethods()){
        System.out.println( m.getName() );
    }
}
 0
Author: Omid.N,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-26 11:12:45

Używam do tego obejścia:

class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();
 -4
Author: nikolai.serdiuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-27 08:45:27